{"record":{"id":"72a8148da07da76c","repo":"DIYgod/RSSHub","slug":"failed-to-retrieve-user-follows-from-mangadex-api-72a814","errorCode":null,"errorMessage":"Failed to retrieve user follows from MangaDex API.","messagePattern":"Failed to retrieve user follows from MangaDex API\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"lib/routes/mangadex/user/follows.ts","lineNumber":114,"sourceCode":"    const { type } = ctx.req.param();\n\n    const followType = (type || 'reading') as FollowType;\n\n    const accessToken = await getToken();\n\n    const statuses = (await cache.tryGet(\n        `mangadex:user-follow-${followType}`,\n        async () => {\n            const response = await got.get(userFollowUrl, {\n                headers: {\n                    Authorization: `Bearer ${accessToken}`,\n                    'User-Agent': config.trueUA,\n                },\n            });\n\n            const statuses = response?.data?.statuses;\n            if (!statuses) {\n                throw new Error('Failed to retrieve user follows from MangaDex API.');\n            }\n\n            return statuses;\n        },\n        config.cache.routeExpire,\n        false\n    )) as Record<string, string>;\n\n    const mangaIds = filterByValue(statuses, statusMap[followType]);\n\n    const mangaMetaMap = await getMangaMetaByIds(mangaIds);\n\n    const mangaChapters = await Promise.all(mangaIds.map((id) => getMangaChapters(id, undefined, 10)));\n\n    const mangas = mangaChapters.flatMap((chapters, index) => {\n        const mangaMeta = mangaMetaMap.get(mangaIds[index]);\n        return chapters.map((chapter) => ({\n            title: mangaMeta?.title ?? 'Unknown',","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/mangadex/user/follows.ts#L96-L132","documentation":"Thrown when GET /manga/status (the logged-in user's reading-status map) returns no response.data.statuses. Cached under mangadex:user-follow-<followType> for routeExpire and filtered by statusMap[followType] (reading/re/on_hold/etc.). Requires a Bearer token.","triggerScenarios":"GET https://api.mangadex.org/manga/status with Authorization: Bearer <accessToken> returning no statuses field: invalid/expired token, user has no reading-status entries, or followType maps to a status the user has none of.","commonSituations":"Auth misconfigured; access token stale; user has never marked any manga with the requested status; cached empty/error result; followType param not in statusMap so statusMap[followType] is undefined and filterByValue returns nothing (though that path returns empty rather than throwing).","solutions":["Set MANGADEX_* credentials and flush mangadex:access-token.","Confirm the account has manga marked with the requested followType at mangadex.org.","Use a valid followType (reading, re_on_hold, plan_to_read, dropped, completed).","Clear cache key mangadex:user-follow-<followType>."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"const VALID_FOLLOW_TYPES = ['reading','re_on_hold','plan_to_read','dropped','completed'];\nif (!VALID_FOLLOW_TYPES.includes(followType)) {\n    throw new Error(`Invalid followType: ${followType}`);\n}\nif (!config.mangadex.clientId || !config.mangadex.clientSecret) {\n    throw new ConfigNotFoundError('User follows require MANGADEX_* auth');\n}","typeGuard":"const isStatusesResponse = (v: unknown): v is { statuses: Record<string, string> } =>\n    typeof v === 'object' && v !== null && typeof (v as any).statuses === 'object';","tryCatchPattern":null,"preventionTips":["Whitelist followType values at the route boundary.","Require MANGADEX auth for follows routes.","Clear mangadex:user-follow-<followType> when behavior changes after re-auth."],"tags":["mangadex","api","authentication","validation"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}