Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/fix-advanced-routing-404-fallback.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes a bug where `experimental.advancedRouting` with `astro/hono` handlers threw `TypeError: Cannot read properties of undefined (reading 'route')` for unmatched routes instead of rendering the custom 404 page.
14 changes: 9 additions & 5 deletions packages/astro/src/core/fetch/fetch-state.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import { AstroCookies } from '../cookies/index.js';
import { type Pipeline, Slots } from '../render/index.js';
import {
ASTRO_GENERATOR,
DEFAULT_404_COMPONENT,
fetchStateSymbol,
originPathnameSymbol,
pipelineSymbol,
Expand All @@ -36,7 +35,7 @@ import { Rewrites } from '../rewrites/handler.js';
import { isRoute404or500, isRouteServerIsland } from '../routing/match.js';
import { normalizeUrl } from '../util/normalized-url.js';
import { getOriginPathname, setOriginPathname } from '../routing/rewrite.js';
import { routeHasHtmlExtension } from '../routing/helpers.js';
import { getCustom404Route, routeHasHtmlExtension } from '../routing/helpers.js';
import type { ResolvedRenderOptions } from '../app/base.js';
import { getRenderOptions } from '../app/render-options.js';
import { getFirstForwardedValue, validateForwardedHeaders } from '../app/validate-headers.js';
Expand Down Expand Up @@ -820,9 +819,14 @@ export class FetchState implements AstroFetchState {

// Fall back to a 404 route so middleware can still run.
if (!this.routeData) {
this.routeData = pipeline.manifestData.routes.find(
(route) => route.component === '404.astro' || route.component === DEFAULT_404_COMPONENT,
);
const custom404 = getCustom404Route(pipeline.manifestData);
// Only use SSR 404 routes here. Prerendered 404 pages are already
// built to static HTML, so the pipeline can't render them at
// runtime. Leaving routeData unset lets the error handler serve
// the pre-built page from disk instead.
if (custom404 && !custom404.prerender) {
this.routeData = custom404;
}
}
if (!this.routeData) {
pipeline.logger.debug('router', "Astro hasn't found routes that match " + this.request.url);
Expand Down
28 changes: 28 additions & 0 deletions packages/astro/test/units/fetch/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ describe('FetchState (astro/fetch)', () => {
assert.equal(state.routeData!.route, '/[b_ssr]');
assert.equal(state.routeData!.prerender, false);
});

it('falls back to the 404 route when no route matches', () => {
const notFoundPage = createPage(simplePage, { route: '/404' });
const app = createTestApp([createPage(simplePage, { route: '/' }), notFoundPage]);
const request = stampApp(new Request('http://example.com/does-not-exist'), app);
const state = new FetchState(request);

assert.ok(state.routeData, 'routeData should fall back to the 404 route');
assert.equal(state.routeData!.route, '/404');
});
});

// #endregion
Expand Down Expand Up @@ -238,6 +248,24 @@ describe('pages()', () => {
assert.match(text, /<h1>Hello<\/h1>/);
});

it('renders the 404 page for unmatched routes instead of throwing', async () => {
const notFoundPage = createComponent((_result: any, _props: any, _slots: any) => {
return render`<h1>Not Found</h1>`;
});
const app = createTestApp([
createPage(simplePage, { route: '/' }),
createPage(notFoundPage, { route: '/404' }),
]);
const request = stampApp(new Request('http://example.com/does-not-exist'), app);
const state = new FetchState(request);

const response = await pages(state);

assert.equal(response.status, 404);
const text = await response.text();
assert.match(text, /<h1>Not Found<\/h1>/);
});

it('renders an endpoint', async () => {
const app = createTestApp([
createEndpoint(
Expand Down
Loading