{"record":{"id":"162f66df8322318b","repo":"DIYgod/RSSHub","slug":"twitter-api-is-not-configured","errorCode":null,"errorMessage":"Twitter API is not configured","messagePattern":"Twitter API is not configured","errorType":"exception","errorClass":"ConfigNotFoundError","httpStatus":503,"severity":"error","filePath":"lib/routes/twitter/api/developer-api/api.ts","lineNumber":66,"sourceCode":"                }).readOnly,\n                isUserAuth: true,\n            });\n        } else {\n            appClients.push({\n                client: new TwitterApi({\n                    appKey: consumerKey,\n                    appSecret: consumerSecret,\n                }).readOnly,\n                isUserAuth: false,\n            });\n        }\n    }\n};\n\nexport const getAppClient = async () => {\n    init();\n    if (!appClients.length) {\n        throw new ConfigNotFoundError('Twitter API is not configured');\n    }\n    index += 1;\n\n    const currentWrapper = appClients[index % appClients.length];\n\n    return currentWrapper.isUserAuth ? currentWrapper.client : await currentWrapper.client.appLogin();\n};\n\nconst mapUserToLegacy = (user: Record<string, any>) =>\n    user\n        ? {\n              id_str: user.id,\n              name: user.name,\n              screen_name: user.username,\n              description: user.description,\n              profile_image_url: user.profile_image_url,\n              profile_image_url_https: user.profile_image_url,\n              url: user.url,","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/twitter/api/developer-api/api.ts#L48-L84","documentation":"ConfigNotFoundError from the Twitter developer-API client pool: after `init()` runs, if no valid consumer key/secret pair was pushed into `appClients`, getAppClient cannot return a client and throws. This is the runtime signal that the Twitter API v2 credentials are missing or all invalid.","triggerScenarios":"`config.twitter.consumerKey` or `config.twitter.consumerSecret` is unset, or every comma-separated pair has an empty secret (lines 37-39 `continue`), leaving appClients empty; getAppClient then throws on line 65-66.","commonSituations":"Self-hosted RSSHub without `TWITTER_CONSUMER_KEY`/`TWITTER_CONSUMER_SECRET`; keys revoked by X; only an auth token (web API) was configured but a route forced the developer API.","solutions":["Apply for a Twitter developer project and create an app with v2 read access.","Set `TWITTER_CONSUMER_KEY` and `TWITTER_CONSUMER_SECRET` (comma-separated for multiple clients) in RSSHub config and restart.","Ensure secrets are non-empty for each key index; mismatches cause silent skips."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"init();\nif (!appClients.length) { throw new ConfigNotFoundError('Set TWITTER_CONSUMER_KEY/TWITTER_CONSUMER_SECRET with at least one valid pair'); }","typeGuard":"const hasClientPool = (a: unknown): a is { length: number } => Array.isArray(a) && a.length > 0;","tryCatchPattern":null,"preventionTips":["Validate consumer key/secret pairs at boot and warn on empty entries","Use a secret manager for credentials","Mark the route requireConfig so callers know"],"tags":["twitter","config","oauth","developer-api","config-not-found"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}