{"record":{"id":"9f578c3c5c3356bb","repo":"DIYgod/RSSHub","slug":"invalid-language-9f578c","errorCode":null,"errorMessage":"Invalid language","messagePattern":"Invalid language","errorType":"validation","errorClass":"InvalidParameterError","httpStatus":null,"severity":"warning","filePath":"lib/routes/myfigurecollection/activity.tsx","lineNumber":76,"sourceCode":"| nl | Nederlands |\n| no | Norsk      |\n| pl | Polski     |\n| pt | Português  |\n| ru | Русский    |\n| sv | Svenska    |\n| zh | 中文       |`,\n};\n\nasync function handler(ctx) {\n    const category = ctx.req.param('category') ?? '-1';\n    const language = ctx.req.param('language') ?? '';\n    const latestAdditions = ctx.req.param('latestAdditions') ?? '1';\n    const latestEdits = ctx.req.param('latestEdits') ?? '1';\n    const latestAlerts = ctx.req.param('latestAlerts') ?? '1';\n    const latestPictures = ctx.req.param('latestPictures') ?? '1';\n\n    if (language && !isValidHost(language)) {\n        throw new InvalidParameterError('Invalid language');\n    }\n\n    const rootUrl = `https://${language === 'en' || language === '' ? '' : `${language}.`}myfigurecollection.net`;\n    const currentUrl = `${rootUrl}/browse.v4.php?mode=activity&latestAdditions=${latestAdditions}&latestEdits=${latestEdits}&latestAlerts=${latestAlerts}&latestPictures=${latestPictures}&rootId=${category}`;\n\n    const response = await got({\n        method: 'get',\n        url: currentUrl,\n    });\n\n    const $ = load(response.data);\n\n    const items = $('.activity-wrapper')\n        .toArray()\n        .map((item) => {\n            const $item = $(item);\n\n            return {","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/myfigurecollection/activity.tsx#L58-L94","documentation":"Thrown by the MyFigureCollection activity route when the :language path parameter is non-empty and fails the isValidHost regex check (a single-label hostname pattern: starts alphanumeric, contains only alphanumeric/hyphen, max 63 chars). The language is used as a subdomain prefix (e.g. {language}.myfigurecollection.net), so it must be a valid DNS label.","triggerScenarios":"Passing a language code with invalid hostname characters: dots (e.g. 'zh-CN'), underscores, spaces, or special chars. Passing a value longer than 63 characters. Passing an empty-but-truthy value like a single space. Valid codes (en, de, es, fr, ja, zh, etc.) pass because they are clean DNS labels.","commonSituations":"User passes 'zh-CN' (with hyphen-region) thinking it is an IETF tag — the hyphen makes it 'zh' label + invalid second label conceptually, but actually the regex allows hyphens so 'zh-CN' would pass; the real failure is codes with dots, underscores, or non-ASCII. User passes a full hostname like 'en.myfigurecollection.net'. User passes uppercase which actually passes (regex is case-insensitive).","solutions":["Use one of the documented two-letter language codes: en, de, es, fi, fr, it, ja, nl, no, pl, pt, ru, sv, zh (or omit for en).","Avoid dots, underscores, spaces, or non-ASCII characters in the language segment.","List valid codes in the error message for discoverability."],"exampleFix":"// before\nif (language && !isValidHost(language)) {\n    throw new InvalidParameterError('Invalid language');\n}\n\n// after — explicit allowlist with helpful message\nconst allowedLangs = ['', 'en', 'de', 'es', 'fi', 'fr', 'it', 'ja', 'nl', 'no', 'pl', 'pt', 'ru', 'sv', 'zh'];\nif (language && !allowedLangs.includes(language.toLowerCase())) {\n    throw new InvalidParameterError(`Invalid language '${language}'. Supported: ${allowedLangs.filter(Boolean).join(', ')}`);\n}","handlingStrategy":"validation","validationCode":"const MFC_LANGUAGES = ['', 'en', 'de', 'es', 'fi', 'fr', 'it', 'ja', 'nl', 'no', 'pl', 'pt', 'ru', 'sv', 'zh'];\nconst language = (ctx.req.param('language') ?? '').toLowerCase();\nif (language && !MFC_LANGUAGES.includes(language)) {\n    throw new InvalidParameterError(`Invalid language '${language}'. Supported: ${MFC_LANGUAGES.filter(Boolean).join(', ')}`);\n}","typeGuard":"const MFC_LANGS = new Set(['', 'en', 'de', 'es', 'fi', 'fr', 'it', 'ja', 'nl', 'no', 'pl', 'pt', 'ru', 'sv', 'zh']);\nfunction isMfcLanguage(l: string): boolean {\n    return MFC_LANGS.has(l.toLowerCase());\n}","tryCatchPattern":null,"preventionTips":["Use an explicit allowlist of valid language codes instead of the generic hostname regex.","Share the allowlist between this route and the sibling index route.","Normalize case before validation.","Include the supported list in the error message."],"tags":["validation","language","hostname","myfigurecollection","parameter"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}