{"record":{"id":"4a92c827ca89c3b5","repo":"DIYgod/RSSHub","slug":"readwise-access-token-is-missing","errorCode":null,"errorMessage":"Readwise access token is missing","messagePattern":"Readwise access token is missing","errorType":"error_code","errorClass":"ConfigNotFoundError","httpStatus":503,"severity":"error","filePath":"lib/routes/readwise/list.ts","lineNumber":65,"sourceCode":"\\`\\`\\`\nhttps://rsshub.app/readwise/list/location=new&category=article\n\\`\\`\\`\n\nfetches articles in the Inbox.\n\n\\`\\`\\`\nhttps://rsshub.app/readwise/list/category=article&tag=shortlist&tag=AI&tagStrategy=all\n\\`\\`\\`\n\nfetches articles tagged both by \\`shortlist\\` and \\`AI\\`.`,\n};\n\nconst TAG_STRATEGY_ALL = 'all'; // Items with tags matching all the given ones can then be returned.\nconst TAG_STRATEGY_ANY = 'any'; // Items with tags matching any of the given ones can be returned.\n\nasync function handler(ctx) {\n    if (!config.readwise || !config.readwise.accessToken) {\n        throw new ConfigNotFoundError('Readwise access token is missing');\n    }\n\n    let apiUrl = 'https://readwise.io/api/v3/list/?';\n    let tag, tagStrategy;\n\n    if (ctx.req.param('routeParams')) {\n        const urlSearchParams = new URLSearchParams(ctx.req.param('routeParams'));\n\n        const location = urlSearchParams.get('location');\n        const category = urlSearchParams.get('category');\n        const updatedAfter = urlSearchParams.get('updatedAfter');\n\n        tag = urlSearchParams.get('tag');\n        tagStrategy = urlSearchParams.get('tagStrategy') === TAG_STRATEGY_ANY || urlSearchParams.get('tagStrategy') === TAG_STRATEGY_ALL ? urlSearchParams.get('tagStrategy') : TAG_STRATEGY_ANY;\n\n        if (location) {\n            apiUrl += `location=${location}&`;\n        }","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/readwise/list.ts#L47-L83","documentation":"The Readwise list route needs a personal access token under `config.readwise.accessToken` to authenticate against `readwise.io/api/v3/list/`. If `config.readwise` itself is undefined or its `accessToken` is empty, the handler refuses to run — Readwise returns 401 for any unauthenticated call and there is no public fallback. This is the only entry point for the route.","triggerScenarios":"A request to /readwise/list/... on an RSSHub instance where `READWISE_ACCESS_TOKEN` was never set. The check runs before any URLSearchParams parsing or API call.","commonSituations":"Self-hosted deploy that skipped the Readwise config block; user assumed the public rsshub.app instance ships with shared credentials (it does not — each user needs their own token); token name typo in env (`READWISE_TOKEN` instead of `READWISE_ACCESS_TOKEN`).","solutions":["Generate a Readwise access token at https://readwise.io/access_token and set `READWISE_ACCESS_TOKEN=<token>` in the RSSHub environment, then restart.","Verify the config block exists; if you load config from a custom file, ensure the `readwise.accessToken` path is populated.","If you do not have an account, the route is simply unavailable — Readwise has no anonymous API."],"exampleFix":"// before\n# no readwise config\n\n// after\n# .env\nREADWISE_ACCESS_TOKEN=xxxxxxxxxxxxxxxx","handlingStrategy":"validation","validationCode":"if (!config.readwise?.accessToken) {\n    return ctx.body('Readwise route requires READWISE_ACCESS_TOKEN.', 503);\n}","typeGuard":"const hasReadwiseToken = (c: typeof config): boolean => Boolean(c.readwise?.accessToken);","tryCatchPattern":null,"preventionTips":["Store the token in a secret manager, not in plaintext config files.","Document the token acquisition URL (https://readwise.io/access_token) next to the route.","Add a startup self-test that pings /api/v3/auth/ with the configured token."],"tags":["config-missing","env-vars","auth-token"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}