{"record":{"id":"8250c13c73790e8e","repo":"jackwener/OpenCLI","slug":"http-userlookup-error-failed-to-resolve-twitte","errorCode":null,"errorMessage":"HTTP ${userLookup.error}: Failed to resolve Twitter user @${targetUser}","messagePattern":"HTTP (.+?): Failed to resolve Twitter user @(.+?)","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/twitter/following.js","lineNumber":201,"sourceCode":"        const headers = {\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        // Get userId from screen_name\n        const userLookup = unwrapBrowserResult(await page.evaluate(async (url, headers) => {\n            const resp = await fetch(url, { headers, credentials: 'include' });\n            if (!resp.ok) return { error: resp.status };\n            const d = await resp.json();\n            return { userId: d.data?.user?.result?.rest_id || null };\n        }, buildUserByScreenNameQueryUrl(userByScreenNameQueryId, targetUser), headers));\n        if (userLookup?.error === 401 || userLookup?.error === 403) {\n            throw new AuthRequiredError('x.com', `Twitter user lookup failed (HTTP ${userLookup.error})`);\n        }\n        if (userLookup?.error) {\n            throw new CommandExecutionError(`HTTP ${userLookup.error}: Failed to resolve Twitter user @${targetUser}`);\n        }\n        const userId = userLookup?.userId || null;\n        if (!userId)\n            throw new CommandExecutionError(`Could not find user @${targetUser}`);\n\n        const allUsers = [];\n        const seen = new Set();\n        let cursor = null;\n        let lastRawResponse = null;\n\n        // Runaway guard only; --limit and cursor exhaustion control normal pagination.\n        for (let i = 0; i < MAX_PAGINATION_PAGES && allUsers.length < limit; i++) {\n            const fetchCount = Math.min(50, limit - allUsers.length + 10);\n            const apiUrl = buildFollowingUrl(followingQueryId, userId, fetchCount, cursor);\n            const data = unwrapBrowserResult(await page.evaluate(async (url, headers) => {\n                const r = await fetch(url, { headers, credentials: 'include' });\n                return r.ok ? await r.json() : { error: r.status };\n            }, apiUrl, headers));","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/twitter/following.js#L183-L219","documentation":"CommandExecutionError thrown when the UserByScreenName lookup fails with an HTTP status other than 401/403 (which are handled as auth errors). The message embeds the actual status code and the target screen name, indicating the request to resolve the user's numeric rest_id failed for a non-auth reason.","triggerScenarios":"The in-page fetch of buildUserByScreenNameQueryUrl(userByScreenNameQueryId, targetUser) returns a status like 400, 404, 429, or 5xx; userLookup.error is truthy but not 401/403, so describe falls to this generic CommandExecutionError.","commonSituations":"Passing a malformed or nonexistent screen name (400/404); rate limiting after many rapid commands (429); stale hardcoded FOLLOWING/USER_BY_SCREEN_NAME query IDs producing 400; Twitter server-side 5xx incidents.","solutions":["Verify the screen name is correct and well-formed (e.g. @elonmusk, no spaces)","Check the HTTP code in the message: 429 means rate limited — wait and retry; 400/404 means the user or query is invalid","Update opencli to refresh Twitter query IDs via resolveTwitterQueryId","Retry later if the code is 5xx (Twitter-side outage)"],"exampleFix":"// before: bad screen name -> HTTP 400\n$ opencli twitter following '@elon musk'\nError: HTTP 400: Failed to resolve Twitter user @@elon musk\n// after\n$ opencli twitter following @elonmusk","handlingStrategy":"try-catch","validationCode":"function validateHandle(user) {\n  const m = /^@?([A-Za-z0-9_]{1,15})$/.exec((user || '').trim());\n  if (!m) throw new Error(`Invalid Twitter handle: ${user}`);\n  return m[1];\n}","typeGuard":"function isTransientHttpStatus(err) {\n  const m = /HTTP (\\d{3})/.exec(String(err && err.message));\n  return !!m && (m[1].startsWith('5') || m[1] === '429');\n}","tryCatchPattern":"try {\n  await cli.following(target);\n} catch (err) {\n  const code = (/(\\d{3})/.exec(err.message) || [])[1];\n  if (code === '429') { await sleep(60000); return retry(target); }\n  if (code && code[0] === '5') { await sleep(5000); return retry(target, 2); }\n  throw err; // 400/404: bad handle or stale query ID — update CLI\n}","preventionTips":["Validate the screen name format before calling","Treat 429 as rate limiting: back off exponentially","Keep opencli updated so resolveTwitterQueryId uses current query IDs","Retry 5xx errors after a short delay"],"tags":["http","twitter-api","rate-limit"],"backgroundTag":"http-request-failed","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}