{"record":{"id":"c73472e27f1d1c3c","repo":"jackwener/OpenCLI","slug":"failed-to-add-username-to-list-listid-ms","errorCode":null,"errorMessage":"Failed to add @${username} to list ${listId}: ${msg.slice(0, 300)}","messagePattern":"Failed to add @(.+?) to list (.+?): (.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/list-add-core.js","lineNumber":68,"sourceCode":"        && !/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);\n    if (!Number.isFinite(memberCountAfter)) {\n        throw new CommandExecutionError(`Failed to add @${username} to list ${listId}: invalid member_count in response`);\n    }\n\n    if (memberCountAfter < memberCountBefore) {\n        throw new CommandExecutionError(\n            `Failed to add @${username} to list ${listId}: member_count decreased unexpectedly (${memberCountBefore} → ${memberCountAfter})`\n        );\n    }\n\n    const countIncreased = memberCountAfter > memberCountBefore;\n    const noop = !countIncreased;","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/list-add-core.js#L50-L86","documentation":"A CommandExecutionError from buildListAddMemberRow thrown when the HTTP call succeeded but the GraphQL response contained fatal errors and no member_count. The library deliberately ignores partial GraphQL errors (e.g. on default_banner_media_results) that X returns even on success; only when the main data (member_count) is missing AND fatal errors exist does it fail, with the first 300 chars of the joined error messages.","triggerScenarios":"addResult.mc is null/undefined and fatalGraphqlErrors(addResult.errors) is non-empty — i.e. X returned a real GraphQL error payload (e.g. unauthorized, rate-limited mutation, list not found) for the add-member mutation.","commonSituations":"Adding to a list you do not own; GraphQL-level account restrictions; X returning a substantive error for the mutation despite HTTP 200.","solutions":["Read the embedded GraphQL error message(s) in the error text — they name the real cause (permissions, rate limit, etc.).","Verify the authenticated account owns the list before mutating.","Refresh session cookies if the error indicates unauthorized access.","Back off and retry if the error indicates rate limiting or temporary unavailability."],"exampleFix":"// before: mutating a list owned by someone else\nawait listAddUser(page, { listId: '999999999', username: 'alice' });\n// after: only mutate your own list\nconst myListIds = await fetchOwnedLists(page);\nif (myListIds.includes('999999999')) await listAddUser(page, { listId: '999999999', username: 'alice' });","handlingStrategy":"try-catch","validationCode":"null","typeGuard":"function hasFatalGraphqlErrors(r) {\n  return Array.isArray(r?.errors) && r.errors.some(e => e && typeof e.message === 'string');\n}","tryCatchPattern":"try {\n  await listAddUser(page, { listId, username });\n} catch (e) {\n  if (/Failed to add @/.test(e.message)) {\n    // message contains the first 300 chars of the GraphQL error(s)\n    console.error('GraphQL error from X:', e.message);\n    if (/unauthorized|forbidden/i.test(e.message)) await refreshSessionCookies(page);\n    if (/rate/i.test(e.message)) await sleep(15 * 60_000);\n  }\n  throw e;\n}","preventionTips":["Only mutate lists you own.","Check the embedded GraphQL error text — it names the real cause.","Back off on rate-limit errors before retrying.","Keep the library updated for X GraphQL schema changes."],"tags":["twitter","graphql","api-error","cli"],"backgroundTag":"graphql-errors-in-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}