{"record":{"id":"966a8fb037bfe4d1","repo":"DIYgod/RSSHub","slug":"invalid-tag-id-tag-id-should-be-a-number","errorCode":null,"errorMessage":"Invalid tag ID. Tag ID should be a number.","messagePattern":"Invalid tag ID\\. Tag ID should be a number\\.","errorType":"validation","errorClass":"InvalidParameterError","httpStatus":503,"severity":"error","filePath":"lib/routes/douyin/hashtag.ts","lineNumber":38,"sourceCode":"        supportBT: false,\n        supportPodcast: false,\n        supportScihub: false,\n    },\n    radar: [\n        {\n            source: ['douyin.com/hashtag/:cid'],\n            target: '/hashtag/:cid',\n        },\n    ],\n    name: '标签',\n    maintainers: ['TonyRL'],\n    handler,\n};\n\nasync function handler(ctx) {\n    const cid = ctx.req.param('cid');\n    if (Number.isNaN(cid)) {\n        throw new InvalidParameterError('Invalid tag ID. Tag ID should be a number.');\n    }\n    const routeParams = Object.fromEntries(new URLSearchParams(ctx.req.param('routeParams')));\n    const embed = fallback(undefined, queryToBoolean(routeParams.embed), false); // embed video\n    const iframe = fallback(undefined, queryToBoolean(routeParams.iframe), false); // embed video in iframe\n    const relay = resolveUrl(routeParams.relay, true, true); // embed video behind a reverse proxy\n\n    const tagUrl = `https://www.douyin.com/hashtag/${cid}`;\n\n    const tagData = await cache.tryGet(\n        `douyin:hashtag:${cid}`,\n        async () => {\n            const context = await playwright();\n            const page = await context.newPage();\n            let awemeList: any = '';\n            await page.route('**/*', (route) => {\n                const request = route.request();\n                request.resourceType() === 'document' || request.resourceType() === 'script' || request.resourceType() === 'xhr' ? route.continue() : route.abort();\n            });","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/DIYgod/RSSHub/blob/bed535e0879dc71c5aff6f1e7bd1ac21ede40115/lib/routes/douyin/hashtag.ts#L20-L56","documentation":"Thrown by the Douyin hashtag route when the `cid` path parameter fails the numeric validation check. However, there is a BUG in the validation: `Number.isNaN(cid)` checks whether `cid` is the value `NaN`, but `cid` is always a string (from `ctx.req.param()`), and `Number.isNaN('any string')` always returns `false`. This means the check NEVER throws — even non-numeric strings like 'abc' pass through and fail later in unexpected ways. The intended check was `Number.isNaN(Number(cid))` or `Number.isNaN(+cid)`.","triggerScenarios":"Supplying a non-numeric cid (e.g. /douyin/hashtag/abc) — but due to the bug, this does NOT trigger the error; instead the invalid cid silently passes and the Playwright request to douyin.com/hashtag/abc fails or returns garbage. The error message is effectively dead code under the current implementation.","commonSituations":"A developer debugging why a non-numeric hashtag ID doesn't produce the expected validation error; fixing the validation to properly reject non-numeric input.","solutions":["Fix the validation: change `Number.isNaN(cid)` to `Number.isNaN(Number(cid))` so non-numeric strings are properly rejected.","If you are a user hitting this after the fix, ensure the cid is the numeric hashtag ID from the Douyin hashtag page URL.","Find the correct cid from the URL: https://www.douyin.com/hashtag/<cid>."],"exampleFix":"// before (BUG: Number.isNaN on a string always returns false)\nconst cid = ctx.req.param('cid');\nif (Number.isNaN(cid)) {\n    throw new InvalidParameterError('Invalid tag ID. Tag ID should be a number.');\n}\n\n// after\nconst cid = ctx.req.param('cid');\nif (Number.isNaN(Number(cid))) {\n    throw new InvalidParameterError('Invalid tag ID. Tag ID should be a number.');\n}","handlingStrategy":"validation","validationCode":"// Correctly validate that cid is a numeric string\nfunction isValidDouyinCid(cid: string): boolean {\n    return /^\\d+$/.test(cid);\n}\n\nconst cid = userInput;\nif (!isValidDouyinCid(cid)) {\n    throw new Error(`Invalid tag ID '${cid}'. Must be a numeric string.`);\n}","typeGuard":"function isNumericCid(value: string): boolean {\n    return /^\\d+$/.test(value);\n}","tryCatchPattern":"try {\n    const feed = await fetch(`${rsshubUrl}/douyin/hashtag/${cid}/routeParams`);\n} catch (e) {\n    if (e.message.includes('Invalid tag ID')) {\n        console.error(`cid must be numeric, got: ${cid}`);\n    }\n    throw e;\n}","preventionTips":["NOTE: The current validation has a bug — Number.isNaN() on a string always returns false. Until fixed, validate client-side with /^\\d+$/.test(cid).","Extract the cid from the Douyin hashtag page URL: https://www.douyin.com/hashtag/<cid>.","If maintaining this route, fix the guard to Number.isNaN(Number(cid))."],"tags":["douyin","invalid-parameter","validation-bug","number-isnan","javascript"],"backgroundTag":null,"analyzedSha":"bed535e0879dc71c5aff6f1e7bd1ac21ede40115","analyzedAt":"2026-08-12T19:29:35.364Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}