ErrLookup › Background articles › InvalidParameterError: RSSHub HTTP 400 "invalid parameter" errors — why route parameter validation fails and how to fix it
InvalidParameterError: RSSHub HTTP 400 "invalid parameter" errors — why route parameter validation fails and how to fix it
InvalidParameterError is the validation error thrown by route handlers when a path parameter fails its guard: an unknown category, subsite, or sort key; a non-hostname-safe value where a subdomain is expected; a malformed composite type string; or a resource that does not exist. It serializes to an HTTP 400 response, so the caller sees a client-error status rather than a crash or a 500. This article maps the whole family across the RSSHub codebase: the validation patterns behind it, the most frequent triggers, the known dead-guard bugs that mask it, and the fixes that hold across routes.
Distilled from 210 documented records across 2 repositories.
Background
InvalidParameterError is produced inside route handlers, before any upstream request is made. Guards fire immediately after the path parameters are extracted, so when the throw happens no network call to the target site has occurred; this distinguishes it from fetch failures, which surface as different error classes. To the caller it looks uniform: RSSHub serializes the error to an HTTP 400 with a localized "invalid parameter" body, and any HTML in the message (such as a docs link) is rendered as-is. Because the check is local, the error is almost always the client's mistake — the wrong value was passed — though a minority of routes use the same error class for existence checks against upstream APIs, which conflates "not found" with "invalid".
Four validation shapes recur across the family. First, membership checks against a fixed set: the handler tests the parameter with Object.hasOwn or Object.keys(...).includes against a config object, categories map, or sortMap, and throws when the key is absent. Second, hostname-shape validation: values that get interpolated into a URL subdomain (https://${language}.eagle.cool, https://${user}.substack.com) are run through a DNS-label regex that rejects dots, underscores, slashes, leading/trailing hyphens, and values over 63 characters. This check is purely structural — an unsupported but well-formed value like a wrong region code passes validation and produces an empty or broken feed instead of an error. Third, composite type strings: parameters like <period>_<genre>_<novelType> are split and each segment validated against its own enum, with some segments optional on one route and mandatory on a sibling route. Fourth, semantic lookups: some handlers call an upstream API (user slug resolution, novel search, channel listing) and throw this same error class when the response indicates the resource does not exist.
The family also documents real defects in the guards themselves. Two routes validate numeric parameters with Number.isNaN(x) where x is always a string, so the check never fires and invalid input flows downstream to fail in confusing ways; one route tests the truthiness of a filter result that is always an array, making the error dead code that yields an empty feed instead. Conversely, some defensive arms are deliberately unreachable in the normal call path — they exist only to catch enum/lookup-map drift introduced by maintainers. Message quality varies: some errors embed the valid values or a docs link, one echoes the wrong path segment, and several are localized in Chinese with exact string matching against Chinese province names or page headings. Where two routes share a similarly named enum, the member sets can differ (a subsite code valid on the ranking route throws on the search route; a ranking period valid in the general list is excluded from the R18 list), so the same value can be valid or invalid depending on the route — the behavior is route-specific, and the records disagree rather than follow one rule.
Common causes
- Value outside the route's allowed set.The category, sort, subsite, or channel key is not a key in the route's config object or map. The check runs before any API call, so it fires fast with no upstream traffic. Examples include a category number in a known gap of the map, an unknown sort key, or a category slug other than the single supported one.
- Malformed composite type string.Parameters like <period>_<genre>_<novelType> fail when the segment order is wrong, a mandatory segment is omitted (some routes default it, siblings require it), or an unknown enum value is used. Extra segments and near-miss values (a valid query parameter mistaken for a ranking novelType) also throw.
- Non-hostname-safe characters in a subdomain parameter.Values interpolated into a URL hostname must pass a DNS-label regex: no dots, underscores, slashes, leading/trailing hyphens, or strings over 63 characters. Passing a full URL, an email, or a locale like zh_cn fails immediately. The check is syntactic only — a well-formed but nonexistent subdomain passes and yields an empty feed.
- Wrong kind of identifier.Passing a numeric user ID or @username where a long prefixed sec_uid is required, a numeric novel ID where an ncode is required, or a full URL where a bare subdomain or slug is expected. Prefix checks (e.g., a fixed base64 header on the ID) reject these on sight.
- Route-specific enum drift.Same-named enums diverge between files: a subsite code valid on the R18 ranking route throws on the search route, and a ranking period valid in the general list is excluded from the R18 list. A value proven on one route cannot be assumed valid on its sibling.
- Exact-match failure against source text.Some routes compare the parameter against exact strings from the target site — simplified-Chinese province and city names, or an expert page's section headings. Transliterations, spacing differences, half-width vs full-width characters, and case differences all fail the comparison.
- Resource genuinely absent.Routes that use this error class for existence checks throw when a novel, author slug, or article channel does not exist — deleted, renamed, private, or temporarily empty. A 400 here means "not found," not "malformed," though a transient empty API response can also trigger it.
- Dead guard masking the real failure.Known bugs make some validations unreachable: Number.isNaN applied to a string parameter always returns false, and a truthiness test on an always-present array never fails. Malformed input then passes validation and fails downstream with unrelated symptoms (redirects, undefined property access, or an empty feed).
What usually fixes it
- Copy parameter values from the route's documented parameter options or example URLs rather than typing from memory; where a route auto-generates its options table from the enums, every listed value is guaranteed valid.
- Pass exactly the component the route asks for: the bare subdomain (not the full URL), the slug or ncode from the current page URL (not a numeric ID), and the full-length prefixed sec_uid where required. Verify existence first by opening the target page in a browser; prefer numeric IDs over slugs where both are accepted, since they skip lookup-based checks entirely.
- For composite type strings, respect the documented segment order and check which segments are optional on that specific route. When a value works on one route but throws on a sibling, suspect route-specific enum subsets rather than a typo, and re-read that route's own valid values.
- When a guard is known-dead (the string NaN check or the array-truthiness bug), validate client-side with a regex such as /^\d+$/ for numeric IDs, and as a maintainer fix the guard to Number.isNaN(Number(x)) or an explicit .length check so the contract actually holds.
- For maintainers: prefer Record<Enum, Value> lookups and explicit allowlists over loosely-typed switches and generic hostname regexes, so the compiler flags missing keys; update parallel maps and enums in the same commit; and include the valid values (or a docs link) in the error message so callers can self-correct.
Go deeper
- HTTP status errors: handling 4xx and 5xx responses — how to handle 4xx and 5xx responses properly.
Documented occurrences
- Invalid Isekai category: ${category}(DIYgod/RSSHub)
- Invalid lang(DIYgod/RSSHub)
- Invalid ranking type: ${type}(DIYgod/RSSHub)
- Invalid genre ranking type: ${type}(DIYgod/RSSHub)
- Invalid Syosetu subsite.\nValid subsites are: yomou, noc, mnlt, mid(DIYgod/RSSHub)
- Invalid subsite: ${sub}(DIYgod/RSSHub)
- 不支持当前Discuz版本.(DIYgod/RSSHub)
- Invalid type(DIYgod/RSSHub)
- Invalid tag ID. Tag ID should be a number.(DIYgod/RSSHub)
- Invalid user(DIYgod/RSSHub)
- Invalid isekai ranking type: ${type}(DIYgod/RSSHub)
- User Not Found(DIYgod/RSSHub)
- Invalid period: ${period}(DIYgod/RSSHub)
- Invalid section name(DIYgod/RSSHub)
- Not found ${type} in ${id}: ${currentUrl}(DIYgod/RSSHub)
- Unknown channel(DIYgod/RSSHub)
- 未找到 ${placeName} 的疫情数据,请检查输入的省市名称是否正确(DIYgod/RSSHub)
- Invalid category(DIYgod/RSSHub)
- Bad category. See <a href="https://docs.rsshub.app/routes/government#guang-dong-sheng-ren-min-zheng-fu-shen-zhen-shi-zhu-fang-he-jian-she-ju">docs</a>(DIYgod/RSSHub)
- Novel not found in both APIs(DIYgod/RSSHub)
…and 190 more across the corpus — use search.
Honest provenance: generated on 2026-08-15 from AI-assisted analysis of the linked records. See how records are made.