Minimal reproduction for an Astro bug: with experimental.advancedRouting enabled and a custom
src/app.ts built from the astro/hono handlers (app.use(pages())), a page that throws at
render time returns a bare Internal Server Error instead of rendering the project's
src/pages/500.astro (HTTP 500).
The custom 404 page is rendered by the same pipeline (since
withastro/astro#16907, shipped in astro@6.4.3),
so the missing 500 handling is an asymmetry in the astro/hono pages() handler.
astro |
6.4.3 |
| adapter | @astrojs/node 10.1.3 (mode: 'standalone', output: 'server') |
hono |
4.12.23 |
| flag | experimental.advancedRouting: true |
| Node | 24.x |
npm install
npm run build
npm start # http://localhost:4321
curl -i http://localhost:4321/ # 200 — home
curl -i http://localhost:4321/does-not-exist # 404 — renders src/pages/404.astro ✅
curl -i http://localhost:4321/boom # 500 — "Internal Server Error" (NOT src/pages/500.astro) ❌GET /boom (a route whose component throws during render) returns HTTP 500 with the body:
Internal Server Error
i.e. Hono's default error response. src/pages/500.astro (<h1>my custom 500</h1>) is not
rendered. The server logs the raw Error: boom stack.
GET /boom should render src/pages/500.astro with HTTP 500, exactly as a standard SSR app does.
To confirm the contrast, remove experimental.advancedRouting from astro.config.mjs and delete
src/app.ts, then rebuild — the same request returns HTTP 500 with <h1>my custom 500</h1>.
| Request | advancedRouting + astro/hono |
plain SSR (no flag, no app.ts) |
|---|---|---|
GET /does-not-exist |
404 — my custom 404 ✅ |
404 — my custom 404 |
GET /boom |
500 — Internal Server Error ❌ |
500 — my custom 500 |
The astro/hono pages() handler (packages/astro/src/core/hono/index.ts) delegates straight to
the fetch pipeline:
function pages() {
return async (context, _honoNext) => {
return fetchPages(getFetchState(context));
};
}fetchPages renders the matched route but does not wrap the render in a try/catch, and never
falls back to getCustom500Route(...). When the page throws, the error propagates out of the Hono
middleware and Hono's default onError returns Internal Server Error.
The standard app pipeline does the opposite: it catches the render error and serves the error page
(packages/astro/src/core/app/index.ts / core/routing/handler.ts →
renderError({ status: 500, ... }), which resolves getCustom500Route(...) and renders
500.astro).
Note the asymmetry: withastro/astro#16907 (astro@6.4.3) added getCustom404Route(...) to the fetch
pipeline so the 404 page is now served for unmatched routes — but the symmetric 500 handling
for render-time errors is still missing.
- withastro/astro#16907 — custom
404.astronot rendered for unmatched routes under the same setup (fixed inastro@6.4.3).