{"record":{"id":"c3cd2710eba46810","repo":"DIYgod/RSSHub","slug":"user-not-found-c3cd27","errorCode":null,"errorMessage":"User not found","messagePattern":"User not found","errorType":"exception","errorClass":"InvalidParameterError","httpStatus":503,"severity":"warning","filePath":"lib/routes/twitter/api/web-api/api.ts","lineNumber":50,"sourceCode":"                method: 'GET',\n                params,\n                headers: {\n                    'accept-encoding': 'gzip',\n                },\n            });\n        }\n\n        return twitterGot(`${baseUrl}${id.startsWith('+') ? gqlMap.UserByRestId : gqlMap.UserByScreenName}`, params, {\n            allowNoAuth: !id.startsWith('+'),\n        });\n    });\n\nconst cacheTryGet = async (_id, params, operationName, func) => {\n    const userData: any = await getUserData(_id);\n    const id = userData.data?.user?.result?.rest_id;\n    if (id === undefined) {\n        cache.set(`twitter-userdata-${_id}`, '', config.cache.contentExpire);\n        throw new InvalidParameterError('User not found');\n    }\n    return cache.tryGet(getTwitterUserCacheKey(id, operationName, params), () => func(id, params), config.cache.routeExpire, false);\n};\n\nconst getUserTweets = (id: string, params?: Record<string, any>) =>\n    cacheTryGet(id, params, 'getUserTweets', async (id, params = {}) =>\n        gatherLegacyFromData(\n            await paginationTweets('UserTweets', id, {\n                ...params,\n                count: 20,\n                includePromotedContent: true,\n                withQuickPromoteEligibilityTweetFields: true,\n                withVoice: true,\n                withV2Timeline: true,\n            })\n        )\n    );\n","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/twitter/api/web-api/api.ts#L32-L68","documentation":"Thrown by RSSHub's Twitter web-api when the GraphQL UserByRestId / UserByScreenName response contains no user.result.rest_id. RSSHub treats a missing rest_id as 'the screen name or numeric id does not resolve to an account', caches an empty placeholder so the lookup is not retried, and surfaces InvalidParameterError('User not found') so the HTTP layer returns a 400-class error to the feed reader.","triggerScenarios":"Calling a Twitter user route (/twitter/user/:id) with a misspelled or deleted screen name, a numeric id of a suspended account, or an id whose response shape changed (Twitter occasionally drops rest_id for protected/age-gated accounts). The check fires only after userData.data.user.result.rest_id === undefined, i.e. the API answered but did not include a rest id.","commonSituations":"Feed URL hard-codes an old handle that the user renamed or deleted; copy-paste of a handle with a leading '@' that the route does not strip; Twitter returns a UserUnavailable payload that still has data.user but no result.rest_id; transient API outage returning a partial JSON body.","solutions":["Verify the screen name resolves on x.com in a browser (open https://x.com/<id>).","Prefix the id with '+' to force the numeric UserByRestId path, or remove the '+' to use screen-name lookup, matching the actual identifier type.","Remove the cached empty placeholder by evicting the cache key 'twitter-userdata-<id>' (contentExpire window) so the lookup is re-attempted after the account is restored.","If the account was renamed, update the feed URL to the new handle."],"exampleFix":"// before\n// /twitter/user/someoldhandle  (renamed)\n// after\n// /twitter/user/newhandle\n\n// or force numeric-id lookup\n// /twitter/user/+1234567890","handlingStrategy":"validation","validationCode":"// Before subscribing, sanity-check the handle resolves.\nimport ofetch from 'ofetch';\nasync function twitterUserExists(handle: string): Promise<boolean> {\n  try {\n    const r = await ofetch(`https://x.com/i/api/graphql/xmU6X_CKVnQ5lSrCbAmJsg/UserByScreenName?variables=${encodeURIComponent(JSON.stringify({ screen_name: handle.replace(/^@/, ''), withSafetyModeUserFields: true }))}`, { headers: { authorization: 'Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D' } });\n    return Boolean(r?.data?.user?.result?.rest_id);\n  } catch { return false; }\n}","typeGuard":"function isTwitterUserResult(userData: any): userData is { data: { user: { result: { rest_id: string } } } } {\n  return typeof userData?.data?.user?.result?.rest_id === 'string';\n}","tryCatchPattern":"// RSSHub operators usually cannot edit the route; end-users should wrap feed fetches.\ntry {\n  await fetchRss('/twitter/user/<handle>');\n} catch (e) {\n  if (/User not found/.test(String(e))) { /* remove/fix the feed */ }\n  else throw e;\n}","preventionTips":["Strip leading '@' from handles before building the feed URL.","Periodically validate stored Twitter handles against x.com since accounts get renamed/suspended.","Use '+<numericId>' form when you have a stable numeric id to avoid screen-name churn."],"tags":["twitter","invalid-parameter","user-lookup","cache"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}