What version of Hono are you using?
4.13.7
What runtime/platform is your app running on? (with version if possible)
Node.js v24.20.0 with @hono/node-server, but the reproduction below uses app.request(), so it looks runtime-independent.
What steps can reproduce the bug?
import { Hono } from 'hono'
const app = new Hono()
app.get('/*', (c) => c.text('matched'))
console.log((await app.request('/a%20b')).status) // 200
console.log((await app.request('/a%0Ab')).status) // 404 <- expected 200
getPath() runs the path through decodeURI before routing, so %0A becomes a real newline. RegExpRouter compiles a wildcard to ONLY_WILDCARD_REG_EXP_STR = '.*' (and TAIL_WILDCARD_REG_EXP_STR = '(?:|/.*)'), and in JavaScript . never matches a line terminator unless the s flag is set. The route therefore cannot match and the request falls through to the not-found handler.
Named parameters are not affected, because LABEL_REG_EXP_STR = '[^/]+' does match those characters:
app.get('/:p', (c) => c.text('matched')) // '/a%0Ab' -> 200
I ran into this with a route that accepts free text in a path segment: any text containing a newline 404s, while everything else works.
What is the expected behavior?
A wildcard route matches any path, including one whose decoded form contains a line terminator, the same way a named parameter already does.
What do you see instead?
A 404 from the not-found handler. The route handler is never called.
The affected characters are exactly the four JavaScript line terminators: \n (%0A), \r (%0D), \u2028 (%E2%80%A8) and \u2029 (%E2%80%A9). Tab, form feed, vertical tab and NUL all match fine.
With app.get('/*', ...) and a request to /a%0Ab:
| Router |
Result |
| SmartRouter (default) |
404 |
| RegExpRouter |
404 |
| TrieRouter |
200 |
| PatternRouter |
200 |
| LinearRouter |
200 |
SmartRouter cannot route around this. It selects a router on the first request and only moves on when add() throws UnsupportedPathError; RegExpRouter registers the route without complaint and merely fails to match later, which is indistinguishable from a genuine 404.
Only the wildcard patterns are affected. A wildcard in the middle of a path, such as /files/*/download, compiles to LABEL_REG_EXP_STR and matches correctly.
Additional information
new Hono({ router: new TrieRouter() }) is a working workaround.
A fix might be to make the wildcard patterns match line terminators, for example '[\s\S]*' in place of '.*' and '(?:|/[\s\S]*)' in place of '(?:|/.*)', or to compile the matcher with the s flag.
What version of Hono are you using?
4.13.7
What runtime/platform is your app running on? (with version if possible)
Node.js v24.20.0 with
@hono/node-server, but the reproduction below usesapp.request(), so it looks runtime-independent.What steps can reproduce the bug?
getPath()runs the path throughdecodeURIbefore routing, so%0Abecomes a real newline.RegExpRoutercompiles a wildcard toONLY_WILDCARD_REG_EXP_STR = '.*'(andTAIL_WILDCARD_REG_EXP_STR = '(?:|/.*)'), and in JavaScript.never matches a line terminator unless thesflag is set. The route therefore cannot match and the request falls through to the not-found handler.Named parameters are not affected, because
LABEL_REG_EXP_STR = '[^/]+'does match those characters:I ran into this with a route that accepts free text in a path segment: any text containing a newline 404s, while everything else works.
What is the expected behavior?
A wildcard route matches any path, including one whose decoded form contains a line terminator, the same way a named parameter already does.
What do you see instead?
A 404 from the not-found handler. The route handler is never called.
The affected characters are exactly the four JavaScript line terminators:
\n(%0A),\r(%0D),\u2028(%E2%80%A8) and\u2029(%E2%80%A9). Tab, form feed, vertical tab and NUL all match fine.With
app.get('/*', ...)and a request to/a%0Ab:SmartRouter cannot route around this. It selects a router on the first request and only moves on when
add()throwsUnsupportedPathError;RegExpRouterregisters the route without complaint and merely fails to match later, which is indistinguishable from a genuine 404.Only the wildcard patterns are affected. A wildcard in the middle of a path, such as
/files/*/download, compiles toLABEL_REG_EXP_STRand matches correctly.Additional information
new Hono({ router: new TrieRouter() })is a working workaround.A fix might be to make the wildcard patterns match line terminators, for example
'[\s\S]*'in place of'.*'and'(?:|/[\s\S]*)'in place of'(?:|/.*)', or to compile the matcher with thesflag.