jackwener/OpenCLI · error · CommandExecutionError
Failed to add @ to list : HTTP
Error message
Failed to add @${username} to list ${listId}: HTTP ${addResult?.status ?? 0}${addResult?.fetchError ? ' (' + addResult.fetchError + ')' : ''}${addResult?.raw ? ' — ' + addResult.raw : ''} What it means
A CommandExecutionError from buildListAddMemberRow in `clis/twitter/list-add-core.js` thrown when the HTTP layer of the X list-add mutation did not succeed (addResult.httpOk is false). The message embeds the HTTP status (0 when unknown) plus any fetchError or raw body text captured for diagnosis.
Solutions
- Refresh your X login cookies and rerun — 401/403 usually means a stale session.
- Check the embedded HTTP status: 429 means back off and retry later; 403 means the account cannot edit this list.
- Verify the listId belongs to the authenticated account (only the list owner can add members).
- Check connectivity/proxy settings if status is 0 with a fetchError.
Example fix
// before: stale cookies produce HTTP 403
await listAddUser(page, { listId: '123456789', username: 'alice' });
// after: refresh session cookies first, then retry
await refreshSessionCookies(page);
await listAddUser(page, { listId: '123456789', username: 'alice' }); Defensive patterns
Strategy: try-catch
Validate before calling
// preflight: verify session and ownership before mutating
const cookies = await page.getCookies({ url: 'https://x.com' });
if (!cookies.some(c => c.name === 'auth_token')) throw new Error('No X auth cookies; refresh session first'); Type guard
function isHttpOkAddResult(r) {
return Boolean(r && typeof r === 'object' && r.httpOk === true);
} Try / catch
try {
await listAddUser(page, { listId, username });
} catch (e) {
const m = /HTTP (\d+)/.exec(e.message);
const status = m && Number(m[1]);
if (status === 429) await sleep(15 * 60_000); // rate limited: back off
else if (status === 401 || status === 403) await refreshSessionCookies(page);
else if (status === 0) await checkConnectivity();
throw e;
} Prevention
- Refresh X cookies regularly; expired sessions produce non-2xx statuses.
- Respect rate limits: pace list mutations and back off on 429.
- Confirm the authenticated account owns the target list.
- Log the raw response embedded in the error to diagnose quickly.
When it happens
Trigger: Calling listAddUser/row where the underlying GraphQL add-member request returns a non-2xx status (e.g. 401/403 auth failure, 429 rate limit) or the fetch itself fails (status 0 with a fetchError).
Common situations: Expired X session cookies; account flagged or lacking permission to edit the list; rate limiting after many mutations; network errors / DNS failures before a response arrives.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- Could not fetch lists
- message (describeTwitterApiError('TweetResultByRestId'…
- 12306 returned non-JSON body
- 12306 queryByTrainNo returned HTTP
- 12306 queryByTrainNo returned non-JSON body
AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29).
Data as JSON: /api/errors/e9c1a31d590b2a6e.
Report an issue: GitHub.
Appendix: source
Thrown at clis/twitter/list-add-core.js:56
standardized_nudges_misinfo: true,
tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,
longform_notetweets_rich_text_read_enabled: true,
longform_notetweets_inline_media_enabled: true,
responsive_web_grok_image_annotation_enabled: true,
responsive_web_enhance_cards_enabled: false,
};
function fatalGraphqlErrors(errors) {
const list = Array.isArray(errors) ? errors : [];
return list.filter((e) =>
!(e?.path || []).join('.').includes('default_banner_media_results')
&& !/decode/i.test(e?.message || '')
);
}
export function buildListAddMemberRow({ addResult, memberCountBefore, listId, username, userId }) {
if (!addResult?.httpOk) {
throw new CommandExecutionError(
`Failed to add @${username} to list ${listId}: HTTP ${addResult?.status ?? 0}${addResult?.fetchError ? ' (' + addResult.fetchError + ')' : ''}${addResult?.raw ? ' — ' + addResult.raw : ''}`
);
}
// X often returns a partial GraphQL error on `default_banner_media_results`
// even on successful mutations. Treat only missing main data or non-decode
// GraphQL errors as command failures.
const hasMemberCount = addResult.mc !== null && addResult.mc !== undefined;
const fatalErrors = fatalGraphqlErrors(addResult.errors);
if (!hasMemberCount && fatalErrors.length) {
const msg = fatalErrors.map((e) => e.message || JSON.stringify(e)).join('; ');
throw new CommandExecutionError(`Failed to add @${username} to list ${listId}: ${msg.slice(0, 300)}`);
}
if (!hasMemberCount) {
throw new CommandExecutionError(`Failed to add @${username} to list ${listId}: no member_count in response`);
}
const memberCountAfter = Number(addResult.mc);View on GitHub (pinned to 49907e53dc)