{"record":{"id":"c34a799f0310383b","repo":"jackwener/OpenCLI","slug":"twitter-createlist-returned-http-result-httpstat","errorCode":null,"errorMessage":"Twitter CreateList returned HTTP ${result.httpStatus}","messagePattern":"Twitter CreateList returned HTTP (.+?)","errorType":"exception","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/twitter/list-create.js","lineNumber":45,"sourceCode":"    }\n    if (name.length > NAME_MAX) {\n        throw new ArgumentError(`List name too long: ${name.length} chars (max ${NAME_MAX})`);\n    }\n    if (description.length > DESCRIPTION_MAX) {\n        throw new ArgumentError(`Description too long: ${description.length} chars (max ${DESCRIPTION_MAX})`);\n    }\n    if (modeRaw !== 'public' && modeRaw !== 'private') {\n        throw new ArgumentError(`Invalid mode: ${JSON.stringify(kwargs.mode)}. Expected \"public\" or \"private\".`);\n    }\n    return { listName: name, listDescription: description, listMode: modeRaw, privateFlag: modeRaw === 'private' };\n}\n\nfunction requireCreateListResult(result, expectedName, expectedMode) {\n    if (!result || typeof result !== 'object') {\n        throw new CommandExecutionError(`Unexpected result from twitter list-create: ${JSON.stringify(result)}`);\n    }\n    if (result.httpStatus === 401 || result.httpStatus === 403) {\n        throw new AuthRequiredError('x.com', `Twitter CreateList returned HTTP ${result.httpStatus}`);\n    }\n    if (!result.ok) {\n        const snippet = String(result.bodyText || '').slice(0, 300);\n        throw new CommandExecutionError(`HTTP ${result.httpStatus} from CreateList: ${snippet}`);\n    }\n    if (!result.bodyJson || typeof result.bodyJson !== 'object') {\n        throw new CommandExecutionError(`CreateList returned malformed JSON payload. Body: ${String(result.bodyText || '').slice(0, 300)}`);\n    }\n    const list = result.bodyJson?.data?.list;\n    if (!list || typeof list !== 'object') {\n        const errors = result.bodyJson?.errors;\n        if (Array.isArray(errors) && errors.length > 0) {\n            throw new CommandExecutionError(`CreateList failed: ${errors[0].message || JSON.stringify(errors[0])}`);\n        }\n        throw new CommandExecutionError(`CreateList returned no list payload. Body: ${String(result.bodyText || '').slice(0, 300)}`);\n    }\n    const id = String(list.id_str || list.id || '');\n    if (!/^\\d+$/.test(id)) {","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/list-create.js#L27-L63","documentation":"This AuthRequiredError is thrown by requireCreateListResult when Twitter's CreateList GraphQL endpoint responds with HTTP 401 (unauthorized) or 403 (forbidden), meaning the session cookies are missing, expired, or lack permission to create lists. It signals that the user must (re)authenticate with x.com before retrying.","triggerScenarios":"Calling `opencli twitter list-create` while not logged into x.com, with an expired session cookie, or with a ct0 cookie that doesn't match the session (CSRF mismatch); account restrictions can also yield 403.","commonSituations":"Stale browser cookies after a logout/password change; running headless automation without a logged-in profile; Twitter rate-limiting or flagging the account (403); corporate proxies stripping cookies.","solutions":["Log into x.com in the browser session used by the CLI (ensure a fresh ct0 cookie exists)","Re-run the command after re-authenticating","If 403 persists despite a valid session, check the account's status/restrictions on x.com","Verify no proxy/extension is stripping cookies from the page context"],"exampleFix":"// before\n// command run with an expired x.com session -> HTTP 401\n// after\n// log into x.com in the automation browser, then:\nopencli twitter list-create \"My List\"","handlingStrategy":"try-catch","validationCode":"const cookies = await page.getCookies({ url: 'https://x.com' });\nconst hasCt0 = cookies.some((c) => c.name === 'ct0');\nif (!hasCt0) {\n  throw new Error('Not logged into x.com: authenticate before creating a list');\n}","typeGuard":"function isAuthErrorForCreateList(e) {\n  return e instanceof AuthRequiredError && /CreateList returned HTTP (401|403)/.test(e.message);\n}","tryCatchPattern":"try {\n  await opencli.twitter.listCreate({ name, description, mode });\n} catch (e) {\n  if (isAuthErrorForCreateList(e)) {\n    await loginToX(); // re-establish session cookies\n    return retryListCreate();\n  }\n  throw e;\n}","preventionTips":["Check the x.com session/ct0 cookie exists before running write commands","Periodically refresh the login in the automation browser profile","Handle AuthRequiredError explicitly in scripts to trigger a re-login flow","Don't conflate 403 from rate limits with a bad session — retry with backoff only after confirming auth is valid"],"tags":["auth","http-401","http-403","twitter"],"backgroundTag":"authentication-required","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}