{"record":{"id":"65cbe86cf6991dd6","repo":"jackwener/OpenCLI","slug":"failed-to-add-username-to-list-listid-memb","errorCode":null,"errorMessage":"Failed to add @${username} to list ${listId}: member_count decreased unexpectedly (${memberCountBefore} → ${memberCountAfter})","messagePattern":"Failed to add @(.+?) to list (.+?): member_count decreased unexpectedly \\((.+?) → (.+?)\\)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/list-add-core.js","lineNumber":80,"sourceCode":"    // 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;\n    if (noop && addResult.isMember !== true) {\n        throw new CommandExecutionError(\n            `Failed to add @${username} to list ${listId}: member_count unchanged and membership was not confirmed`\n        );\n    }\n    const verifiedBy = `member_count ${memberCountBefore} → ${memberCountAfter}`;\n    return {\n        listId,\n        username,\n        userId: String(userId),\n        status: noop ? 'noop' : 'success',\n        message: noop","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/list-add-core.js#L62-L98","documentation":"A CommandExecutionError from buildListAddMemberRow thrown when the list's member_count after the add is LOWER than before. An add operation can never reduce the count, so this is treated as verification failure — the mutation likely did not apply as intended or the counts were read from inconsistent states.","triggerScenarios":"memberCountAfter < memberCountBefore after an add — typically caused by reading the before-count long before the mutation and something else removing members, or reading the after-count from a stale/cached response.","commonSituations":"Another session/process removing members concurrently; X serving a stale cached count; a race between two automated list operations.","solutions":["Rerun the command — with a consistent read the count should be >= before.","Ensure no other process/session is mutating the same list concurrently.","Add a short delay between reading the before-count and the mutation, and verify the after-count comes from the mutation response itself.","Check whether the target list was deleted, cleared, or its ownership changed."],"exampleFix":"// before: long gap between count read and mutation lets count drift\nconst before = await getMemberCount(page);\nawait slowOtherWork();\nawait listAddUser(page, { listId, username });\n// after: do them back-to-back\nconst before = await getMemberCount(page);\nawait listAddUser(page, { listId, username });","handlingStrategy":"retry","validationCode":"null","typeGuard":null,"tryCatchPattern":"try {\n  return await listAddUser(page, { listId, username });\n} catch (e) {\n  if (/member_count decreased unexpectedly/.test(e.message)) {\n    // likely a stale/racing count read: rerun with a fresh before-count\n    await sleep(10_000);\n    return listAddUser(page, { listId, username });\n  }\n  throw e;\n}","preventionTips":["Don't mutate the same list from multiple concurrent sessions/scripts.","Read the before-count immediately before the mutation.","Verify the after-count comes from the mutation response, not a cached page.","Check the list wasn't cleared or deleted by another process."],"tags":["twitter","state-mismatch","race-condition","cli"],"backgroundTag":"unexpected-state-change","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}