{"record":{"id":"c91fcf194ecca6e8","repo":"DIYgod/RSSHub","slug":"cannot-get-access-token-since-mangadex-refresh-tok","errorCode":null,"errorMessage":"Cannot get access token since MangaDex refresh token is not set.","messagePattern":"Cannot get access token since MangaDex refresh token is not set\\.","errorType":"exception","errorClass":"ConfigNotFoundError","httpStatus":null,"severity":"error","filePath":"lib/routes/mangadex/_access.ts","lineNumber":90,"sourceCode":"\n    const refreshToken = response?.data?.refresh_token;\n    const accessToken = response?.data?.access_token;\n\n    if (!refreshToken || !accessToken) {\n        throw new Error('Failed to retrieve refresh token from MangaDex API.');\n    }\n\n    config.mangadex.refreshToken = refreshToken; // cache the refresh token\n    return accessToken;\n};\n\nconst getAccessTokenByRefreshToken = async () => {\n    if (!config.mangadex.clientId || !config.mangadex.clientSecret) {\n        throw new ConfigNotFoundError('Cannot get access token since MangaDex client ID or secret is not set.');\n    }\n\n    if (!config.mangadex.refreshToken) {\n        throw new ConfigNotFoundError('Cannot get access token since MangaDex refresh token is not set.');\n    }\n\n    const response = await got.post(constants.API.TOKEN, {\n        headers: {\n            'User-Agent': config.trueUA,\n        },\n        form: {\n            grant_type: 'refresh_token',\n            refresh_token: config.mangadex.refreshToken,\n            client_id: config.mangadex.clientId,\n            client_secret: config.mangadex.clientSecret,\n        },\n    });\n\n    const accessToken = response?.data?.access_token;\n    if (!accessToken) {\n        throw new Error('Failed to retrieve access token from MangaDex API.');\n    }","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/mangadex/_access.ts#L72-L108","documentation":"ConfigNotFoundError raised inside getAccessTokenByRefreshToken when config.mangadex.refreshToken is falsy. In the normal flow getToken routes falsy refreshToken to the username/password path (line 32), so this guard is effectively a defensive check that is only reachable if getAccessTokenByRefreshToken is invoked directly or config.mangadex.refreshToken becomes falsy between the two reads.","triggerScenarios":"Direct invocation of getAccessTokenByRefreshToken with no MANGADEX_REFRESH_TOKEN configured; or a race where config.mangadex.refreshToken was truthy at getToken line 32 but cleared by the time line 89 runs (extremely unlikely given config is a synchronous singleton).","commonSituations":"Operator expected a refresh-token-only flow but never set MANGADEX_REFRESH_TOKEN; a custom route or test imported the non-exported function directly.","solutions":["If you intend to use the refresh-token flow, set MANGADEX_REFRESH_TOKEN to a valid token obtained from a prior password grant.","Otherwise ensure MANGADEX_USERNAME and MANGADEX_PASSWORD are set so getToken takes the credentials path.","Do not import getAccessTokenByRefreshToken directly; route all auth through the default getToken export.","If you set refreshToken at runtime, do it before the first cache populate (key mangadex:access-token)."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const useRefreshFlow = Boolean(config.mangadex.refreshToken);\nif (useRefreshFlow && (!config.mangadex.clientId || !config.mangadex.clientSecret)) {\n    throw new ConfigNotFoundError('Refresh-token flow needs MANGADEX_CLIENT_ID and MANGADEX_CLIENT_SECRET');\n}\nif (useRefreshFlow && !config.mangadex.refreshToken) {\n    // unreachable via getToken, but guard direct callers\n    throw new ConfigNotFoundError('Set MANGADEX_REFRESH_TOKEN or username/password');\n}","typeGuard":"const hasRefreshFlow = (c: typeof config): boolean =>\n    Boolean(c.mangadex?.refreshToken && c.mangadex?.clientId && c.mangadex?.clientSecret);","tryCatchPattern":null,"preventionTips":["Do not call getAccessTokenByRefreshToken directly; use getToken().","If setting refreshToken at runtime, do so before the first cache.tryGet for mangadex:access-token.","Keep username/password configured as a fallback even when using the refresh flow."],"tags":["mangadex","authentication","config","config-not-found"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}