jackwener/OpenCLI · error · ArgumentError
Invalid Twitter/X username
Error message
Invalid Twitter/X username: ${JSON.stringify(part.trim())} What it means
parseBatchUsernames validates each comma-separated token against USERNAME_RE after stripping leading '@' characters and throws ArgumentError for any token that does not match. Twitter/X screen names allow only alphanumerics and underscores, so spaces, hyphens, dots, slashes, or URLs fail validation. The JSON.stringify of the raw token in the message identifies the offending part.
Solutions
- Replace each item with the bare screen name (letters, digits, underscore only): '@alice,@bob_99', not 'https://x.com/alice'.
- Strip URLs/prefixes before invoking, e.g. sed 's#https://x.com/##g'.
- Check the input source for invisible characters (CRLF, quotes); trim and normalize line endings.
- Fix only the token named in the message — tokens that reduce to empty after '@'-stripping are skipped, not errors.
Example fix
// before opencli twitter follow-batch 'https://x.com/alice,@bob-1' // after opencli twitter follow-batch 'alice,bob_1'
Defensive patterns
Strategy: validation
Validate before calling
const USERNAME_RE = /^[A-Za-z0-9_]{1,15}$/;
function sanitizeUsernames(input) {
return String(input ?? '')
.split(',')
.map((p) => p.trim().replace(/^@+/, ''))
.filter((u) => u && !USERNAME_RE.test(u) ? (console.error(`Skipping invalid: ${u}`), false) : Boolean(u));
}
// Pre-strip URLs: input.replaceAll(/https?:\/\/x\.com\//g, '') Type guard
function isValidUsername(u) {
return typeof u === 'string' && /^[A-Za-z0-9_]{1,15}$/.test(u);
} Try / catch
try {
await followBatch(rawUsernames);
} catch (e) {
if (e.code === 'ARGUMENT' && e.message.startsWith('Invalid Twitter/X username')) {
const bad = e.message.match(/"(.*)"/)?.[1];
console.error(`Remove or fix the invalid token: ${bad}`);
} else throw e;
} Prevention
- Strip profile URLs to screen names before passing them in.
- Normalize input: trim, remove quotes, fix CRLF line endings from Windows sources.
- Pre-validate each token with /^[A-Za-z0-9_]{1,15}$/ in your pipeline.
- Keep names bare — no '@' needed (stripped anyway), no hyphens/dots (not allowed on X).
When it happens
Trigger: Passing a full profile URL (https://x.com/alice) instead of the screen name; tokens with hyphens/dots; stray quotes or punctuation around names; tokens with embedded spaces or invisible characters (CRLF) that break the regex.
Common situations: Copy-pasting profile URLs into the username list; piping usernames from another tool with trailing punctuation; Windows line endings adding hidden \r characters; typos like 'bo b'.
Related errors
- Invalid listId: . Expected numeric ID.
- twitter list-add username is required
- twitter tweets --limit must be an integer between 1 and
- twitter tweets --page-delay must be an integer between 0…
- A search query is required.
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/5558c14b71ccd8ce.
Report an issue: GitHub.
Appendix: source
Thrown at clis/twitter/follow-batch.js:21
import { unwrapBrowserResult } from './shared.js';
const USERNAME_RE = /^[A-Za-z0-9_]{1,15}$/;
const DEFAULT_DELAY_MS = 3000;
const MAX_DELAY_MS = 60000;
export function parseBatchUsernames(input) {
const raw = String(input || '').trim();
if (!raw) {
throw new ArgumentError('At least one Twitter/X username is required');
}
const usernames = [];
const seen = new Set();
for (const part of raw.split(',')) {
const username = part.trim().replace(/^@+/, '');
if (!username) continue;
if (!USERNAME_RE.test(username)) {
throw new ArgumentError(`Invalid Twitter/X username: ${JSON.stringify(part.trim())}`);
}
const key = username.toLowerCase();
if (seen.has(key)) continue;
seen.add(key);
usernames.push(username);
}
if (!usernames.length) {
throw new ArgumentError('At least one Twitter/X username is required');
}
return usernames;
}
async function readFollowState(page, username) {
return unwrapBrowserResult(await page.evaluate(`(async () => {
try {
let attempts = 0;
while (attempts < 20) {View on GitHub (pinned to 49907e53dc)