Summary
In a compiled hono app, a POST request is dispatched to the GET handler (or 404s) — the router selects the wrong handler by method. c.req.method is correct inside the handler, so it's not method-value loss. It is build-layout-dependent: the same routes route correctly or incorrectly depending on unrelated surrounding code, which makes this another manifestation of the heap/codegen-layout memory-corruption family (cf. the "premature empty-200 / dropped dynamic response" reports). This breaks every POST endpoint — in our CMS it makes POST /admin/login unusable (login can't complete).
Environment
- perry main 0.5.1195 (
f30a2f5c8), macOS arm64, hono ^4.10.0.
Repro A — mis-dispatch (compile & run this exact file)
repro.ts:
import { Hono } from 'hono';
const app = new Hono();
app.post('/ponly', (c) => c.text('PONLY'));
app.get('/both', (c) => c.text('GET-both'));
app.post('/both', (c) => c.text('POST-both'));
async function d(label: string, path: string, withBody: boolean): Promise<void> {
const opts: RequestInit = withBody
? { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: 'a=1' }
: { method: 'POST' };
const res = await app.fetch(new Request('http://h' + path, opts));
const t = await res.text();
console.log(`${label}: status=${res.status} ${JSON.stringify(t.slice(0, 30))}`);
}
async function main(): Promise<void> {
await d('POST /ponly', '/ponly', false);
await d('POST /both ', '/both', false);
}
void main();
package.json: { "type":"module", "dependencies":{"hono":"^4.10.0"}, "perry":{"compilePackages":["hono"]} }
Actual:
POST /ponly: status=404 "404 Not Found" <-- expected 200 "PONLY"
POST /both : status=200 "GET-both" <-- expected 200 "POST-both" (hit the GET handler!)
c.req.method echoes correctly (verified with an app.all('*') fallback) — so the method value is fine; the router picks the wrong handler.
Repro B — proof it's layout-dependent (the smoking gun)
The identical routes (/ponly, /both) wrapped in a small helper + with the router imports present route correctly:
import { Hono } from 'hono';
import { RegExpRouter } from 'hono/router/reg-exp-router';
import { TrieRouter } from 'hono/router/trie-router';
async function test(name: string, app: Hono) {
app.post('/ponly', (c) => c.text('PONLY'));
app.get('/both', (c) => c.text('GET-both'));
app.post('/both', (c) => c.text('POST-both'));
const r2 = await app.fetch(new Request('http://h/both', { method: 'POST' }));
console.log(name, await r2.text());
}
await test('default', new Hono());
await test('regexp', new Hono({ router: new RegExpRouter() }));
await test('trie', new Hono({ router: new TrieRouter() }));
Actual (all correct): default POST-both | regexp POST-both | trie POST-both.
Same routes, same handlers, same router — only the surrounding code differs — yet Repro A mis-routes and Repro B is correct. So this is not a router-logic bug and not RegExpRouter-specific; it's layout/heap-sensitive corruption. Switching routers is therefore not a reliable workaround.
Real-world reliable trigger
The full Skelpo CMS (Hono SSR, ~/projects/skelpo-cms) reproduces deterministically: compile src/server.ts with perry main, boot, then curl -X POST .../admin/login returns an empty 200 (POST mis-dispatched; the actual handler's redirect/Set-Cookie never runs), while GET /admin/login renders fine. Happy to share the binary/build for a layout-stable trigger.
Impact
All non-GET routing is unreliable under sufficiently large/complex builds — POST/PUT/DELETE may hit a GET handler or 404. Blocks login and every mutating endpoint of a Hono app on perry-native.
Summary
In a compiled hono app, a POST request is dispatched to the GET handler (or 404s) — the router selects the wrong handler by method.
c.req.methodis correct inside the handler, so it's not method-value loss. It is build-layout-dependent: the same routes route correctly or incorrectly depending on unrelated surrounding code, which makes this another manifestation of the heap/codegen-layout memory-corruption family (cf. the "premature empty-200 / dropped dynamic response" reports). This breaks every POST endpoint — in our CMS it makesPOST /admin/loginunusable (login can't complete).Environment
f30a2f5c8), macOS arm64, hono ^4.10.0.Repro A — mis-dispatch (compile & run this exact file)
repro.ts:package.json:{ "type":"module", "dependencies":{"hono":"^4.10.0"}, "perry":{"compilePackages":["hono"]} }Actual:
c.req.methodechoes correctly (verified with anapp.all('*')fallback) — so the method value is fine; the router picks the wrong handler.Repro B — proof it's layout-dependent (the smoking gun)
The identical routes (
/ponly,/both) wrapped in a small helper + with the router imports present route correctly:Actual (all correct):
default POST-both | regexp POST-both | trie POST-both.Same routes, same handlers, same router — only the surrounding code differs — yet Repro A mis-routes and Repro B is correct. So this is not a router-logic bug and not RegExpRouter-specific; it's layout/heap-sensitive corruption. Switching routers is therefore not a reliable workaround.
Real-world reliable trigger
The full Skelpo CMS (Hono SSR,
~/projects/skelpo-cms) reproduces deterministically: compilesrc/server.tswith perry main, boot, thencurl -X POST .../admin/loginreturns an empty 200 (POST mis-dispatched; the actual handler's redirect/Set-Cookie never runs), whileGET /admin/loginrenders fine. Happy to share the binary/build for a layout-stable trigger.Impact
All non-GET routing is unreliable under sufficiently large/complex builds — POST/PUT/DELETE may hit a GET handler or 404. Blocks login and every mutating endpoint of a Hono app on perry-native.