{"record":{"id":"e5bc0e7da2b4d7aa","repo":"jackwener/OpenCLI","slug":"could-not-resolve-user-username-e5bc0e","errorCode":null,"errorMessage":"Could not resolve user @${username}","messagePattern":"Could not resolve user @(.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/list-remove-core.js","lineNumber":83,"sourceCode":"        const ct0 = cookies.find((c) => c.name === 'ct0')?.value || null;\n        if (!ct0) throw new AuthRequiredError('x.com', 'Not logged into x.com (no ct0 cookie)');\n\n        const userByScreenNameQueryId = await resolveTwitterQueryId(page, 'UserByScreenName', USER_BY_SCREEN_NAME_QUERY_ID);\n        const headers = JSON.stringify({\n            'Authorization': `Bearer ${decodeURIComponent(TWITTER_BEARER_TOKEN)}`,\n            'X-Csrf-Token': ct0,\n            'X-Twitter-Auth-Type': 'OAuth2Session',\n            'X-Twitter-Active-User': 'yes',\n        });\n\n        const userLookupUrl = buildUserByScreenNameQueryUrl(userByScreenNameQueryId, username);\n        const userId = unwrapBrowserResult(await page.evaluate(`async () => {\n            const resp = await fetch(${JSON.stringify(userLookupUrl)}, { headers: ${headers}, credentials: 'include' });\n            if (!resp.ok) return null;\n            const d = await resp.json();\n            return d.data?.user?.result?.rest_id || null;\n        }`));\n        if (!userId) throw new CommandExecutionError(`Could not resolve user @${username}`);\n\n        // Resolve listId → name so we can match the dialog row.\n        const listsQueryId = await resolveTwitterQueryId(page, 'ListsManagementPageTimeline', LISTS_MANAGEMENT_QUERY_ID);\n        const listsUrl = `/i/api/graphql/${listsQueryId}/ListsManagementPageTimeline?features=${encodeURIComponent(JSON.stringify(LISTS_MANAGEMENT_FEATURES))}`;\n        const listsData = unwrapBrowserResult(await page.evaluate(`async () => {\n            const r = await fetch(${JSON.stringify(listsUrl)}, { headers: ${headers}, credentials: 'include' });\n            if (!r.ok) return { __error: 'HTTP ' + r.status };\n            return await r.json();\n        }`));\n        if (listsData && listsData.__error) {\n            throw new CommandExecutionError(`Could not fetch lists: ${listsData.__error}`);\n        }\n        const parsedLists = parseListsManagement(listsData, new Set());\n        const targetList = parsedLists.find((l) => l.id === listId);\n        if (!targetList) {\n            throw new CommandExecutionError(`List ${listId} not found among your lists.`);\n        }\n        const targetName = targetList.name;","sourceCodeStart":65,"sourceCodeEnd":101,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/list-remove-core.js#L65-L101","documentation":"listRemoveUser first resolves the target screen name to a numeric user ID via the UserByScreenName GraphQL endpoint executed inside the browser page. If the fetch fails (non-OK status), the response shape is unexpected, or rest_id is absent, it returns null and this CommandExecutionError is thrown. It means the library could not map @username to a Twitter user ID, so the removal cannot proceed.","triggerScenarios":"Calling twitter list-remove with a username whose UserByScreenName fetch returns a non-OK HTTP status (401/403 auth issues, 404 unknown user, 429 rate limit) or a payload without data.user.result.rest_id (suspended/deactivated account, protected/withheld user, or an API shape change after Twitter rotates the query ID).","commonSituations":"Typo in the handle; the account was renamed, suspended, or deleted; the ct0 cookie/session is stale so Twitter returns 401/403; hard rate-limiting (429); Twitter changed the UserByScreenName response schema; a hardcoded query ID went stale.","solutions":["Verify the username is correct and the account still exists by visiting https://x.com/<username> in a browser.","Re-login to x.com in the controlled browser so cookies (ct0, auth_token) are fresh, then retry.","Check whether the request is rate-limited (HTTP 429) and wait before retrying.","Update the UserByScreenName query ID (USER_BY_SCREEN_NAME_QUERY_ID) to the current value from x.com's web app if Twitter rotated it.","Confirm the logged-in session can view the target account (age/region blocks and withheld accounts resolve to null)."],"exampleFix":"// before\nawait cli.run('twitter list-remove', { listId: '12345', username: '@flwer' });\n// after\nawait cli.run('twitter list-remove', { listId: '12345', username: '@flower' }); // corrected handle; account exists","handlingStrategy":"validation","validationCode":"// Pre-check the handle and session before invoking\nconst username = '@flower'.replace(/^@/, '').trim();\nif (!/^[A-Za-z0-9_]{1,15}$/.test(username)) throw new Error('Invalid Twitter handle');\nconst cookies = await page.getCookies({ url: 'https://x.com' });\nif (!cookies.some(c => c.name === 'ct0')) throw new Error('Not logged into x.com');","typeGuard":"function isResolvedUser(v) {\n  return typeof v === 'object' && v !== null && typeof v.rest_id === 'string' && /^\\d+$/.test(v.rest_id);\n}","tryCatchPattern":"try {\n  await listRemoveUser(page, { listId, username });\n} catch (e) {\n  if (e instanceof CommandExecutionError && e.message.includes('Could not resolve user')) {\n    // verify handle exists / refresh session, then retry\n  } else throw e;\n}","preventionTips":["Validate the handle format before calling.","Confirm the account exists by visiting its profile first.","Keep the x.com session fresh; re-login when cookies age out.","Watch for 429s and space out automated calls."],"tags":["twitter","user-lookup","graphql","authentication","browser-automation"],"backgroundTag":"twitter-user-lookup-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}