{"record":{"id":"af0dbcb916b5fc2f","repo":"DIYgod/RSSHub","slug":"nhentai-rss-with-torrents-is-disabled-due-to-the-l","errorCode":null,"errorMessage":"nhentai RSS with torrents is disabled due to the lack of <a href=\"https://docs.rsshub.app/deploy/config#route-specific-configurations\">relevant config</a>","messagePattern":"nhentai RSS with torrents is disabled due to the lack of <a href=\"https://docs\\.rsshub\\.app/deploy/config#route-specific-configurations\">relevant config</a>","errorType":"validation","errorClass":"ConfigNotFoundError","httpStatus":null,"severity":"error","filePath":"lib/routes/nhentai/util.tsx","lineNumber":106,"sourceCode":"        }\n        throw error;\n    }\n};\n\nconst getSimple = async (url) => {\n    const data = await fetchPage(url);\n    const $ = load(data);\n\n    return $('.gallery a.cover')\n        .toArray()\n        .map((ele) => parseSimpleDetail($(ele)));\n};\n\nconst getDetails = (cache, simples, limit) => Promise.all(simples.slice(0, limit).map((simple) => cache.tryGet(simple.link, () => getDetail(simple))));\n\nconst getTorrents = async (cache, simples, limit) => {\n    if (!config.nhentai || !config.nhentai.username || !config.nhentai.password) {\n        throw new ConfigNotFoundError('nhentai RSS with torrents is disabled due to the lack of <a href=\"https://docs.rsshub.app/deploy/config#route-specific-configurations\">relevant config</a>');\n    }\n    const cookie = await getCookie(config.nhentai.username, config.nhentai.password, cache);\n    if (!cookie) {\n        throw new ConfigNotFoundError('Invalid username (or email) or password for nhentai torrent download');\n    }\n    return getTorrentWithCookie(cache, simples, cookie, limit);\n};\nconst getTorrentWithCookie = (cache, simples, cookie, limit) => Promise.all(simples.slice(0, limit).map((simple) => cache.tryGet(simple.link + 'download', () => getTorrent(simple, cookie))));\n\nconst parseSimpleDetail = ($ele) => {\n    const link = new URL($ele.attr('href'), baseUrl).href;\n    const thumb = $ele.children('img');\n    const thumbSrc = thumb.attr('data-src') || thumb.attr('src');\n    const highResoThumbSrc = thumbSrc\n        .replace('thumb', '1')\n        .replace(/t(\\d+)\\.nhentai\\.net/, 'i$1.nhentai.net')\n        .replace('.webp.webp', '.webp');\n    return {","sourceCodeStart":88,"sourceCodeEnd":124,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/nhentai/util.tsx#L88-L124","documentation":"A ConfigNotFoundError raised by getTorrents when the nhentai torrent feature is requested but the deployment has not supplied nhentai credentials. RSSHub uses ConfigNotFoundError specifically to signal a missing server-side configuration (env: NHENTAI_USERNAME / NHENTAI_PASSWORD), distinct from invalid user input, so the operator (not the subscriber) must act. The HTML link in the message points operators to the route-specific config docs.","triggerScenarios":"Calling a nhentai route with torrent/enclosure output enabled (e.g. the torrent download path) while config.nhentai is undefined or lacks username/password. Concretely, the check `!config.nhentai || !config.nhentai.username || !config.nhentai.password` short-circuits true.","commonSituations":"Self-hosted RSSHub deployed without setting NHENTAI_USERNAME/NHENTAI_PASSWORD in the environment; the credentials were removed during a config cleanup; the route is being exercised on a public rsshub.app instance where the feature is intentionally disabled.","solutions":["Set NHENTAI_USERNAME and NHENTAI_PASSWORD in the RSSHub environment (.env / container env) per https://docs.rsshub.app/deploy/config#route-specific-configurations and restart.","Verify the keys are read under config.nhentai.* by the config loader (the config namespace must be 'nhentai').","If the feature is not needed, stop subscribing to the torrent-producing route variant so getTorrents is never invoked."],"exampleFix":"// before\nif (!config.nhentai || !config.nhentai.username || !config.nhentai.password) {\n    throw new ConfigNotFoundError('nhentai RSS with torrents is disabled ...');\n}\n\n// after — no code change; fix is environment-side:\n// .env\n// NHENTAI_USERNAME=your_username\n// NHENTAI_PASSWORD=your_password","handlingStrategy":"validation","validationCode":"// Check config readiness before invoking any torrent-producing nhentai route.\nfunction nhentaiConfigured(): boolean {\n    return Boolean(config.nhentai?.username && config.nhentai?.password);\n}\nif (routeNeedsTorrents && !nhentaiConfigured()) {\n    // skip torrent enrichment, return the non-torrent feed instead of throwing\n}","typeGuard":"const hasNhentaiCreds = (c: typeof config): c is typeof config & { nhentai: { username: string; password: string } } =>\n    Boolean(c.nhentai && c.nhentai.username && c.nhentai.password);","tryCatchPattern":"import ConfigNotFoundError from '@/errors/types/config-not-found';\ntry {\n    items = await getTorrents(cache, simples, limit);\n} catch (e) {\n    if (e instanceof ConfigNotFoundError) {\n        // feature intentionally disabled — return the feed without torrent enclosures\n        items = await getDetails(cache, simples, limit);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Document the required NHENTAI_USERNAME/NHENTAI_PASSWORD env vars in your deployment checklist.","Gate the torrent route behind a feature check so non-torrent subscribers never hit it.","Run a startup config probe that logs which optional features are enabled."],"tags":["config","credentials","config-not-found","env"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}