{"record":{"id":"6bd48d2f2069b899","repo":"DIYgod/RSSHub","slug":"invalid-isekai-category-category","errorCode":null,"errorMessage":"Invalid Isekai category: ${category}","messagePattern":"Invalid Isekai category: (.+?)","errorType":"validation","errorClass":"InvalidParameterError","httpStatus":503,"severity":"warning","filePath":"lib/routes/syosetu/ranking-isekai.ts","lineNumber":51,"sourceCode":"        lim: Math.ceil((limit / 2) * 1.2),\n    };\n\n    if (novelType !== NovelType.TOTAL) {\n        searchParams.type = novelType;\n    }\n\n    switch (category) {\n        case IsekaiCategory.RENAI:\n            searchParams.biggenre = BigGenre.Renai;\n            break;\n        case IsekaiCategory.FANTASY:\n            searchParams.biggenre = BigGenre.Fantasy;\n            break;\n        case IsekaiCategory.OTHER:\n            searchParams.biggenre = `${BigGenre.Bungei}-${BigGenre.Sf}-${BigGenre.Sonota}` as unknown as Join<BigGenre>;\n            break;\n        default:\n            throw new InvalidParameterError(`Invalid Isekai category: ${category}`);\n    }\n\n    return searchParams;\n}\n\nexport async function handleIsekaiRanking(type: string, limit: number): Promise<Data> {\n    const { period, category, novelType } = parseIsekaiRankingType(type);\n    const rankingUrl = `https://yomou.syosetu.com/rank/isekailist/type/${type}`;\n    const rankingTitle = `[${periodToJapanese[period]}] 異世界転生/転移${isekaiCategoryToJapanese[category]}ランキング - ${novelTypeToJapanese[novelType]} BEST${limit}`;\n\n    const searchParams = getIsekaiSearchParams(period, category, novelType, limit);\n    const api = new NarouNovelFetch();\n\n    const [tenseiResult, tenniResult] = await Promise.all([new SearchBuilder({ ...searchParams, istensei: 1 }, api).execute(), new SearchBuilder({ ...searchParams, istenni: 1 }, api).execute()]);\n\n    const combinedNovels = [...tenseiResult.values, ...tenniResult.values];\n    const uniqueNovels = new Map(combinedNovels.map((novel) => [novel.ncode, novel])).values().toArray();\n","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/syosetu/ranking-isekai.ts#L33-L69","documentation":"Defensive default arm in getIsekaiSearchParams (syosetu/ranking-isekai.ts:51) thrown as an InvalidParameterError when the category switch falls through. In the NORMAL call path this is UNREACHABLE: the sole caller handleIsekaiRanking always invokes parseIsekaiRankingType first, which already validates category against IsekaiCategory and throws [525] on failure. This guard only fires if getIsekaiSearchParams is called directly with an unvalidated category, or if a new IsekaiCategory member is added to the enum without a matching case here (TypeScript exhaustiveness is not enforced because the switch is on a loosely-typed value).","triggerScenarios":"A maintainer adds a new IsekaiCategory value to types/ranking.ts without adding the corresponding case in getIsekaiSearchParams; new code calls getIsekaiSearchParams directly, bypassing parseIsekaiRankingType.","commonSituations":"Maintainer extends the enum and forgets the switch arm; refactor introduces a second call site that skips validation.","solutions":["If you added an IsekaiCategory member, add the matching case in getIsekaiSearchParams (mapping to the correct BigGenre).","Never call getIsekaiSearchParams without first running the type through parseIsekaiRankingType."],"exampleFix":"// before (new enum member added, switch falls through)\ncase IsekaiCategory.NEW_VALUE:\n    // missing -> hits default throw\n// after\ncase IsekaiCategory.NEW_VALUE:\n    searchParams.biggenre = BigGenre.SomeGenre;\n    break;","handlingStrategy":"validation","validationCode":"// Unreachable in normal flow; only relevant when extending the enum.\n// Ensure every enum member has a switch case at compile time.\nfunction assertExhaustive(category: IsekaiCategory): never {\n  throw new Error(`Unhandled IsekaiCategory: ${category as string}`);\n}","typeGuard":"import { IsekaiCategory } from './types/ranking';\n\nconst CATEGORY_TO_BIGGENRE: Record<IsekaiCategory, unknown> = {\n  [IsekaiCategory.RENAI]: 'renai',\n  [IsekaiCategory.FANTASY]: 'fantasy',\n  [IsekaiCategory.OTHER]: 'bungei-sf-sonota',\n};\n\nfunction hasBigGenreMapping(category: string): category is IsekaiCategory {\n  return Object.hasOwn(CATEGORY_TO_BIGGENRE, category as IsekaiCategory);\n}","tryCatchPattern":null,"preventionTips":["Never call getIsekaiSearchParams without first validating via parseIsekaiRankingType.","When adding an IsekaiCategory member, add the switch case in the same commit.","Prefer a Record<Enum, Value> lookup over a switch so the compiler flags missing keys."],"tags":["unreachable","defensive-guard","enum-sync","syosetu","maintainer"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}