Minimal reproduction for an Astro bug (filed as withastro/astro#16907):
With
experimental.advancedRoutingenabled and a customsrc/app.tsbuilt from theastro/honohandlers, any unmatched path throwsTypeError: Cannot read properties of undefined (reading 'route')(served as HTTP 500) instead of renderingsrc/pages/404.astro(HTTP 404).
astro |
6.4.2 (also present on main) |
| adapter | @astrojs/node 10.1.2 (mode: 'standalone', output: 'server') |
hono |
4.12.23 |
| flag | experimental.advancedRouting: true |
| Node | 24.16 |
npm install
npm run build
npm start # http://localhost:4321
curl -i http://localhost:4321/ # 200 OK (home renders)
curl -i http://localhost:4321/does-not-exist # 500 Internal Server Error (should be 404)
curl -i http://localhost:4321/boom # 500, but does NOT render 500.astro (see note below)GET /does-not-exist returns HTTP 500 Internal Server Error, and the server logs:
TypeError: Cannot read properties of undefined (reading 'route')
at FetchState.getActionAPIContext (.../dist/server/chunks/server_<hash>.mjs)
at FetchState.getAPIContext (.../dist/server/chunks/server_<hash>.mjs)
at pages$1 (.../dist/server/chunks/server_<hash>.mjs)
at Hono.fetch (.../node_modules/hono/dist/hono-base.js)
at App.render (.../dist/server/chunks/server_<hash>.mjs)
GET /does-not-exist should render src/pages/404.astro with HTTP 404, exactly as the
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 404
with <h1>my custom 404</h1>.
FetchState.#resolveRouteData() resolves the route eagerly. For an unmatched path the 404
fallback (packages/astro/src/core/fetch/fetch-state.ts, ~L822–826 on main) matches by
component name:
this.routeData = pipeline.manifestData.routes.find(
(route) => route.component === '404.astro' || route.component === DEFAULT_404_COMPONENT,
);But a built manifest stores the component as a path ("src/pages/404.astro"), so find()
returns undefined. getActionAPIContext() then dereferences this.routeData!.route (~L955)
→ TypeError.
A candidate fix (astrobot-houston, branch flue/fix-16907) replaces the component-name
comparison with the existing getCustom404Route() helper, which matches the 404 route by route
path (/404):
- this.routeData = pipeline.manifestData.routes.find(
- (route) => route.component === '404.astro' || route.component === DEFAULT_404_COMPONENT,
- );
+ this.routeData = getCustom404Route(pipeline.manifestData);Install the prerelease build of that fix and rebuild:
npm i https://pkg.pr.new/astro@e9126d7
npm run build && npm startThe 404 crash is gone:
| Request | stock 6.4.2 |
fix e9126d7 |
|---|---|---|
GET / |
200 — home | 200 — home |
GET /does-not-exist |
500 (TypeError) | 404 — my custom 404 ✅ |
GET /boom |
500 — repeated "Internal Server Error" | 500 — repeated "Internal Server Error" (unchanged) |
getCustom404Route() matches /404 only (not /500), so the fallback still correctly resolves to
the 404 page even when a 500.astro is also present.
This repo also includes src/pages/500.astro and a deliberately-throwing src/pages/boom.astro.
Under experimental.advancedRouting + astro/hono pages(), a request to a route that throws at
render time returns HTTP 500 but does not render src/pages/500.astro — the response body
is the literal string Internal Server Error repeated many times (≈31× in this repro). This is
identical before and after the #16907 fix — it is a different code path (a matched route that
throws, vs. the unmatched-route 404 fallback), so it is a separate, pre-existing issue rather than
a regression of the fix.