{"record":{"id":"e9c1a31d590b2a6e","repo":"jackwener/OpenCLI","slug":"failed-to-add-username-to-list-listid-http","errorCode":null,"errorMessage":"Failed to add @${username} to list ${listId}: HTTP ${addResult?.status ?? 0}${addResult?.fetchError ? ' (' + addResult.fetchError + ')' : ''}${addResult?.raw ? ' — ' + addResult.raw : ''}","messagePattern":"Failed to add @(.+?) to list (.+?): HTTP (.+?)(.+?)(.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/list-add-core.js","lineNumber":56,"sourceCode":"    standardized_nudges_misinfo: true,\n    tweet_with_visibility_results_prefer_gql_limited_actions_policy_enabled: true,\n    longform_notetweets_rich_text_read_enabled: true,\n    longform_notetweets_inline_media_enabled: true,\n    responsive_web_grok_image_annotation_enabled: true,\n    responsive_web_enhance_cards_enabled: false,\n};\n\nfunction fatalGraphqlErrors(errors) {\n    const list = Array.isArray(errors) ? errors : [];\n    return list.filter((e) =>\n        !(e?.path || []).join('.').includes('default_banner_media_results')\n        && !/decode/i.test(e?.message || '')\n    );\n}\n\nexport function buildListAddMemberRow({ addResult, memberCountBefore, listId, username, userId }) {\n    if (!addResult?.httpOk) {\n        throw new CommandExecutionError(\n            `Failed to add @${username} to list ${listId}: HTTP ${addResult?.status ?? 0}${addResult?.fetchError ? ' (' + addResult.fetchError + ')' : ''}${addResult?.raw ? ' — ' + addResult.raw : ''}`\n        );\n    }\n\n    // X often returns a partial GraphQL error on `default_banner_media_results`\n    // even on successful mutations. Treat only missing main data or non-decode\n    // GraphQL errors as command failures.\n    const hasMemberCount = addResult.mc !== null && addResult.mc !== undefined;\n    const fatalErrors = fatalGraphqlErrors(addResult.errors);\n    if (!hasMemberCount && fatalErrors.length) {\n        const msg = fatalErrors.map((e) => e.message || JSON.stringify(e)).join('; ');\n        throw new CommandExecutionError(`Failed to add @${username} to list ${listId}: ${msg.slice(0, 300)}`);\n    }\n    if (!hasMemberCount) {\n        throw new CommandExecutionError(`Failed to add @${username} to list ${listId}: no member_count in response`);\n    }\n\n    const memberCountAfter = Number(addResult.mc);","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/list-add-core.js#L38-L74","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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."],"exampleFix":"// before: stale cookies produce HTTP 403\nawait listAddUser(page, { listId: '123456789', username: 'alice' });\n// after: refresh session cookies first, then retry\nawait refreshSessionCookies(page);\nawait listAddUser(page, { listId: '123456789', username: 'alice' });","handlingStrategy":"try-catch","validationCode":"// preflight: verify session and ownership before mutating\nconst cookies = await page.getCookies({ url: 'https://x.com' });\nif (!cookies.some(c => c.name === 'auth_token')) throw new Error('No X auth cookies; refresh session first');","typeGuard":"function isHttpOkAddResult(r) {\n  return Boolean(r && typeof r === 'object' && r.httpOk === true);\n}","tryCatchPattern":"try {\n  await listAddUser(page, { listId, username });\n} catch (e) {\n  const m = /HTTP (\\d+)/.exec(e.message);\n  const status = m && Number(m[1]);\n  if (status === 429) await sleep(15 * 60_000); // rate limited: back off\n  else if (status === 401 || status === 403) await refreshSessionCookies(page);\n  else if (status === 0) await checkConnectivity();\n  throw e;\n}","preventionTips":["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."],"tags":["twitter","http","auth","network"],"backgroundTag":"http-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}