jackwener/OpenCLI · error · ArgumentError
Invalid listId: ${JSON.stringify(kwargs.listId)}. Expected n
Error message
Invalid listId: ${JSON.stringify(kwargs.listId)}. Expected numeric ID. What it means
listRemoveUser validates that kwargs.listId is a non-empty string of digits before doing anything. Anything else (undefined, empty, an @handle, a list URL/slug, or a numeric JS value stringified oddly) triggers this ArgumentError from clis/twitter/list-remove-core.js:56. It's a fail-fast input contract: X list mutations need the numeric list REST id, e.g. '1234567890123456789'.
Source
Thrown at clis/twitter/list-remove-core.js:56
longform_notetweets_inline_media_enabled: true,
responsive_web_grok_image_annotation_enabled: true,
responsive_web_enhance_cards_enabled: false,
};
export function interpretRemoveResponse(status, json) {
if (status === 200 && json && (json.id_str || json.id || json.slug)) return { ok: true };
if (json && Array.isArray(json.errors) && json.errors.length > 0) {
const err = json.errors[0];
return { ok: false, error: `${err.code ? '[' + err.code + '] ' : ''}${err.message || 'Unknown error'}` };
}
return { ok: false, error: `HTTP ${status}` };
}
export async function listRemoveUser(page, kwargs) {
const listId = String(kwargs.listId || '').trim();
const username = String(kwargs.username || '').replace(/^@/, '').trim();
if (!listId || !/^\d+$/.test(listId)) {
throw new ArgumentError(`Invalid listId: ${JSON.stringify(kwargs.listId)}. Expected numeric ID.`);
}
if (!username) throw new ArgumentError('twitter list-remove username is required');
// Strategy.UI does not get a domain URL pre-nav from the framework.
// This page context is load-bearing for pre-target GraphQL calls below.
await page.goto('https://x.com');
await page.wait(3);
const cookies = await page.getCookies({ url: 'https://x.com' });
const ct0 = cookies.find((c) => c.name === 'ct0')?.value || null;
if (!ct0) throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');
const userByScreenNameQueryId = await resolveTwitterQueryId(page, 'UserByScreenName', USER_BY_SCREEN_NAME_QUERY_ID);
const headers = JSON.stringify({
'Authorization': `Bearer ${decodeURIComponent(TWITTER_BEARER_TOKEN)}`,
'X-Csrf-Token': ct0,
'X-Twitter-Auth-Type': 'OAuth2Session',
'X-Twitter-Active-User': 'yes',
});View on GitHub (pinned to 49907e53dc)
Solutions
- Pass the numeric list id, e.g. `opencli twitter list-remove 1734567890123456789 someuser`
- Get the correct id from `opencli twitter lists` (the listId column)
- Strip a pasted URL down to the digit-only segment, or add a pre-parse that extracts /i/lists/(\d+)
- Quote the argument in your shell so it isn't dropped or mangled
- If calling listRemoveUser programmatically, coerce with String(value).trim() and pre-test /^\d+$/ before invoking
Example fix
// before opencli twitter list-remove https://x.com/i/lists/1734567890123456789 alice // after opencli twitter list-remove 1734567890123456789 alice
Defensive patterns
Strategy: validation
Validate before calling
function normalizeListId(raw) {
const m = String(raw ?? '').match(/(\d+)\/?$/); // tolerate URLs like /i/lists/123456
const id = m ? m[1] : String(raw ?? '').replace(/[^\d]/g, '');
if (!/^\d+$/.test(id)) throw new Error(`listId must be numeric, got: ${JSON.stringify(raw)}`);
return id;
}
// call: await removeUser(normalizeListId(inputUrlOrId), username); Type guard
function isValidListId(v) {
return typeof v === 'string' || typeof v === 'number';
}
function isNumericListId(v) {
return isValidListId(v) && /^\d+$/.test(String(v).trim());
} Try / catch
try {
await run(`opencli twitter list-remove ${listId} ${username}`);
} catch (e) {
if (e instanceof ArgumentError && /Invalid listId/.test(e.message)) {
// recover: fetch valid ids and retry with the right one
const lists = await run('opencli twitter lists');
const match = lists.find(l => l.name === expectedName);
if (match) return run(`opencli twitter list-remove ${match.listId} ${username}`);
}
throw e;
} Prevention
- Always source listId from `opencli twitter lists`, never from a pasted URL or slug
- Validate with /^\d+$/ before invoking
- Quote arguments in shell to avoid empty/mangled values
- Don't confuse listId with the owner's user id or the list slug
When it happens
Trigger: Calling the list-remove command/function with: no listId argument; listId='' or whitespace; listId as a list slug like 'my-list'; a full URL like 'https://x.com/i/lists/123456' pasted whole; a listId with non-digit characters (letters, commas, '@'); listId passed as a number that lost precision or was formatted with separators like '1,234,567'.
Common situations: Copy-pasting the list page URL instead of the numeric id; supplying the list's @slug from a share link; shell quoting stripping the value so kwargs.listId is empty; mixing up listId with the owner's user id; scripting the CLI and passing null/undefined when a lookup step upstream returned nothing.
Related errors
- twitter list-remove username is required
- <train-no> "${trainNo}" does not look like a 12306 internal
- --seat-types must contain only 12306 seat letters/digits (A-
- ${label} must be a numeric Coupang product ID
- coords must be in "x,y" format (e.g. "1,2")
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/3fff5d47a51c6b5f.
Report an issue: GitHub.