Repository: honojs/hono (2025-07-31T07:35:28Z)

Issue:
Hono throws when using optional catch-all route
### What version of Hono are you using?

latest

### What runtime/platform is your app running on? (with version if possible)

Bun

### What steps can reproduce the bug?

The following code can reproduce the issue:
```ts
import { Hono } from 'hono'

const app = new Hono().basePath('/files')

app.get('/:wildcard{.*}?', (c) => {
  const { wildcard } = c.req.param()

  return c.text(`You requested the path: ${wildcard || '/'}`)
});

export default app
```

Hono only throws when hitting the root with a trailing slash, e.g. `http://localhost:3000/files/`.

There is no issue with `http://localhost:3000/files`, `http://localhost:3000/files/a` or even `http://localhost:3000/files/a/`

### What is the expected behavior?

I didn't expect an error here, since it works without basePath set, but also works on `http://localhost:3000/files/a/`

### What do you see instead?

```
181 |       matchResult,
182 |       env,
183 |       executionCtx,
184 |       notFoundHandler: this.#notFoundHandler
185 |     });
186 |     if (matchResult[0].length === 1) {
                          ^
TypeError: undefined is not an object (evaluating 'matchResult[0].length')
```

### Additional information

If the Hono App doesn't utilize `basePath` then there is no issues at all.


Discussion:
Hi @luxass This is a bug. Thank you for the report. I'll take a look at it.



Patch:
diff --git a/src/router/reg-exp-router/node.ts b/src/router/reg-exp-router/node.ts
index 91abea7f8..ce5214202 100644
--- a/src/router/reg-exp-router/node.ts
+++ b/src/router/reg-exp-router/node.ts
@@ -81,6 +81,9 @@ export class Node {
       const name = pattern[1]
       let regexpStr = pattern[2] || LABEL_REG_EXP_STR
       if (name && pattern[2]) {
+        if (regexpStr === '.*') {
+          throw PATH_ERROR
+        }
         regexpStr = regexpStr.replace(/^\((?!\?:)(?=[^)]+\)$)/, '(?:') // (a|b) => (?:a|b)
         if (/\((?!\?:)/.test(regexpStr)) {
           // prefix(?:a|b) is allowed, but prefix(a|b) is not
diff --git a/src/router/trie-router/node.ts b/src/router/trie-router/node.ts
index 3cb35f649..08d199154 100644
--- a/src/router/trie-router/node.ts
+++ b/src/router/trie-router/node.ts
@@ -155,12 +155,12 @@ export class Node<T> {
             continue
           }
 
-          if (!part) {
+          const [key, name, matcher] = pattern
+
+          if (!part && !(matcher instanceof RegExp)) {
             continue
           }
 
-          const [key, name, matcher] = pattern
-
           const child = node.#children[key]
 
           // `/js/:filename{[a-z]+.js}` => match /js/chunk/123.js


Test patch:
diff --git a/src/router/common.case.test.ts b/src/router/common.case.test.ts
index e6912ef8b..f2225c012 100644
--- a/src/router/common.case.test.ts
+++ b/src/router/common.case.test.ts
@@ -144,6 +144,26 @@ export const runTest = ({
         expect(res.length).toBe(0)
       })
 
+      it('Parameter with {.*} regexp', () => {
+        router.add('GET', '/files/:name{.*}', 'file')
+        let res = match('GET', '/files')
+        expect(res.length).toBe(0)
+
+        res = match('GET', '/files/a')
+        expect(res.length).toBe(1)
+        expect(res[0].handler).toEqual('file')
+        expect(res[0].params['name']).toEqual('a')
+
+        res = match('GET', '/files/a/b')
+        expect(res.length).toBe(1)
+        expect(res[0].handler).toEqual('file')
+        expect(res[0].params['name']).toEqual('a/b')
+
+        res = match('GET', '/files/')
+        expect(res.length).toBe(1)
+        expect(res[0].handler).toEqual('file')
+      })
+
       it('/*', async () => {
         router.add('GET', '/api/*', 'auth middleware')
         router.add('GET', '/api', 'top')
diff --git a/src/router/linear-router/router.test.ts b/src/router/linear-router/router.test.ts
index dbce870fb..396d3c560 100644
--- a/src/router/linear-router/router.test.ts
+++ b/src/router/linear-router/router.test.ts
@@ -10,6 +10,7 @@ describe('LinearRouter', () => {
         tests: [
           'Multi match > `params` per a handler > GET /entry/123/show',
           'Capture regex pattern has trailing wildcard > GET /foo/bar/file.html',
+          'Complex > Parameter with {.*} regexp',
         ],
       },
       {
diff --git a/src/router/reg-exp-router/router.test.ts b/src/router/reg-exp-router/router.test.ts
index 305cee272..736c2ca98 100644
--- a/src/router/reg-exp-router/router.test.ts
+++ b/src/router/reg-exp-router/router.test.ts
@@ -14,6 +14,7 @@ describe('RegExpRouter', () => {
           'Capture Group > Complex capturing group > GET request',
           'Capture complex multiple directories > GET /part1/middle-b/latest',
           'Capture complex multiple directories > GET /part1/middle-b/end-c/latest',
+          'Complex > Parameter with {.*} regexp',
         ],
       },
       {