I went to Claude in the end and established that this is a bug in this version of the blog on Joomla 5.
I was then able to get the csv direct from the database. here is how to do it for anyone with the same error:
Workaround: exporting subscribers when "Export Subscribers" fails with "A query must be a string or a Joomla\Database\QueryInterface instance, a array was given"
The built-in export fails on some Joomla 4/5 setups. The subscribers are stored in the #__easyblog_subscriptions table, so you can export them directly.
Find your details first. Your site's configuration.php has the database name ($db), the database user ($user), the password ($password) and the table prefix ($dbprefix, e.g. abc12_).
Option 1: terminal/SSH (Python 3.6+). Edit the four settings at the top, then paste the whole block into the terminal. It asks for your database password:
python3 - <<'EOF'
# ---- EDIT THESE ----
DB_NAME = "your_database_name"
DB_USER = "your_database_user"
PREFIX = "abc12_"
OUT = "subscribers_export.csv"
# --------------------
import csv, subprocess, os, getpass
env = dict(os.environ, MYSQL_PWD=getpass.getpass("Database password: "))
q = ("SELECT TRIM(REPLACE(REPLACE(fullname, CHAR(13), ''), CHAR(10), '')), "
"TRIM(REPLACE(REPLACE(email, CHAR(13), ''), CHAR(10), '')), created "
"FROM " + PREFIX + "easyblog_subscriptions WHERE utype='site' ORDER BY created")
res = subprocess.run(["mysql", "-u", DB_USER, DB_NAME, "--batch", "--skip-column-names", "-e", q],
stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, env=env)
if res.returncode != 0:
raise SystemExit(res.stderr)
seen, rows, dup, lines = set(), [], 0, 0
for line in res.stdout.split("\n"):
if not line:
continue
lines += 1
name, email, created = (line.split("\t") + ["", "", ""])[:3]
name = "" if name == "NULL" else name.strip()
email = email.strip()
if not email:
continue
if email.lower() in seen:
dup += 1
continue
seen.add(email.lower())
rows.append([name, email, created])
with open(OUT, "w", newline="", encoding="utf-8-sig") as f:
w = csv.writer(f)
w.writerow(["Name", "Email", "Subscribed"])
w.writerows(rows)
os.chmod(OUT, 0o600)
print("Read %d rows. Exported %d subscribers to %s (skipped %d duplicates)" % (lines, len(rows), os.path.abspath(OUT), dup))
EOF
The CSV is saved in the folder you ran it from. Download it, then delete it from the server, since it contains personal data.
Option 2: phpMyAdmin (no terminal needed). Select your database, open the SQL tab, and run the query below, replacing abc12_ with your prefix. Then scroll to the bottom of the results, click Export, choose CSV and tick "Put columns names in the first row".
Tip: in my case, about half the email addresses had a hidden carriage-return character at the end, probably from an earlier import. It caused duplicates and broken rows in a normal export. Both methods above strip it out.
Some tables have other subscription types besides site, such as blogger or category subscriptions. To see which yours has, run:
SELECT utype, COUNT(*) FROM abc12_easyblog_subscriptions GROUP BY utype;
Change utype='site' if you need a different type.
One caveat: the phpMyAdmin query groups by name and email together. If the same email was saved with two different names, it could still appear twice. The Python version handles that properly, so it's the more reliable of the two.