Skip to content

Fix custom 500 page rendering for render-time errors with experimental.advancedRouting astro/hono handlers#17041

Merged
matthewp merged 3 commits into
withastro:mainfrom
iseraph-dev:fix/advanced-routing-500-fallback
Jun 16, 2026
Merged

Fix custom 500 page rendering for render-time errors with experimental.advancedRouting astro/hono handlers#17041
matthewp merged 3 commits into
withastro:mainfrom
iseraph-dev:fix/advanced-routing-500-fallback

Conversation

@iseraph-dev

@iseraph-dev iseraph-dev commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Changes

Under experimental.advancedRouting, the composable astro/hono pages() handler delegated straight to PagesHandler.handle(), with none of the app-level error handling that AstroHandler provides on the standard path. A page throwing during render therefore propagated to the host framework, which answered with its own default error response (Hono: plain-text Internal Server Error) instead of rendering the custom 500.astro page (#16952).

  • Added PagesHandler.handleWithErrorFallback(app, state) (core/pages/handler.ts): wraps route dispatch in a try/catch that logs the error and renders it via app.renderError(..., { status: 500, error }), the same behaviour AstroHandler.render() has on the standard path. Forwarding error keeps the documented error prop on 500.astro working. The logic lives in the handler module because core/fetch/index.ts deliberately keeps its exports as thin, logic-free wrappers.
  • When no route matched, handleWithErrorFallback() returns a 404 response marked with X-Astro-Error (the same pattern handle() uses for the un-dispatched redirect case), so the app's existing post-check renders the 404 error page a level up. This case is reachable because Fix 404 route resolution for experimental.advancedRouting with astro/hono handlers #16911 intentionally leaves routeData unset when the custom 404 route is prerendered (or absent), where PagesHandler.handle() would otherwise throw a TypeError and the catch would turn those unmatched requests into 500s. The 500 path keeps calling renderError() directly: the marker cannot carry the error object, so 500.astro would lose its error prop and the stack would not be logged.
  • Switched pages() in core/fetch/index.ts to delegate to the new method (delegation only, no logic added).
  • Added a changeset (astro patch).

Testing

  • Added 'renders the custom 500 page when a page throws during render' - verifies the full pages() pipeline returns HTTP 500 with the custom 500 page content when a page throws during render
  • Added 'returns a marked 404 for the app post-check when the custom 404 route is prerendered' - verifies unmatched requests return HTTP 404 carrying the X-Astro-Error marker instead of throwing
  • Verified end-to-end against the reproduction from Custom 500.astro not rendered for render-time errors under experimental.advancedRouting + astro/hono pages() #16952 (Hono + app.use(pages()), node standalone): GET /boom -> 500 with the custom 500 page (previously Hono's plain-text Internal Server Error), GET /does-not-exist -> 404 serving the prerendered 404 page with the marker stripped from the final response, GET / -> 200; the thrown error is logged with its stack trace

Out of scope (noticed while testing)

Errors thrown by Astro middleware itself (src/middleware.ts) on the composable path (app.use(middleware())) still reach the host framework's default error handler instead of 500.astro. On the standard path, AstroHandler.render()'s try/catch wraps the whole middleware chain, so middleware errors get the same 500.astro treatment. The equivalent catch can't simply live inside the composable middleware(): its next is the host's next(), so it would also intercept errors thrown by host middleware running below it in the chain and hide them from the user's own app.onError. The all-in-one astro() handler already provides full parity. A possible follow-up would be an error-page helper exported from astro/hono (e.g. app.onError(onError())) so composable users can opt in - happy to open a separate issue for this.

Docs

Closes #16952

@changeset-bot

changeset-bot Bot commented Jun 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 00f9f9f

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 389 packages
Name Type
astro Patch
@e2e/astro-linked-lib Patch
@e2e/actions-blog Patch
@e2e/actions-react-19 Patch
@e2e/astro-component Patch
@e2e/astro-envs Patch
@e2e/astro-island-hydration-error Patch
@test/astro-cloudflare-node-prerender-mdx Patch
@test/astro-cloudflare Patch
@e2e/content-collections Patch
@e2e/csp-server-islands Patch
@e2e/css Patch
@test/custom-client-directives Patch
@e2e/dev-toolbar Patch
@e2e/error-cyclic Patch
@e2e/error-sass Patch
@e2e/errors Patch
@e2e/hydration-race Patch
@e2e/i18n Patch
@test/nested-style-bug-e22e Patch
@e2e/preact-compat-component Patch
@e2e/preact-component Patch
@e2e/preact-lazy-component Patch
@e2e/prefetch Patch
@e2e/react-component Patch
@e2e/server-islands-key Patch
@e2e/server-islands Patch
@e2e/solid-circular Patch
@e2e/solid-component Patch
@e2e/solid-recurse Patch
@e2e/svelte-component Patch
@e2e/e2e-tailwindcss Patch
@e2e/ts-resolution Patch
@e2e/view-transitions Patch
@e2e/vite-virtual-modules Patch
@e2e/vue-component Patch
@performance/md Patch
@performance/mdoc Patch
@performance/mdx Patch
@test/0-css Patch
fake-astro-library Patch
@test/actions Patch
@test/alias-path-alias-style Patch
@test/ts-paths-no-baseurl Patch
@test/aliases-tsconfig Patch
@test/aliases Patch
@test/api-routes Patch
@test/asset-query-params-chunks Patch
@test/asset-url-base Patch
@test/astro-pages Patch
@test/astro-assets-prefix Patch
@test/astro-assets Patch
@test/astro-basic Patch
@test/astro-check-errors Patch
@test/astro-check-no-errors Patch
@test/astro-check-watch Patch
@test/astro-children Patch
@test/astro-client-only Patch
@test/astro-component-bundling Patch
@test/astro-component-code Patch
@test/astro-css-bundling Patch
@test/astro-dev-headers Patch
@test/astro-dev-http2 Patch
@test/astro-doctype Patch
@test/astro-dynamic Patch
@test/astro-env-content-collections Patch
@test/astro-env-required-public Patch
@test/astro-env-server-fail Patch
@test/astro-env-server-secret Patch
@test/astro-env Patch
@test/astro-envs Patch
@test/astro-expr Patch
@test/astro-get-static-paths Patch
@test/astro-head Patch
@test/astro-manifest-client-script Patch
@test/astro-manifest-invalid Patch
@test/astro-manifest Patch
@test/astro-markdown-frontmatter-injection Patch
@test/astro-markdown-plugins Patch
@test/astro-markdown-remarkRehype Patch
@test/astro-markdown-skiki-default-color Patch
@test/astro-markdown-skiki-langs Patch
@test/astro-markdown-skiki-themes-custom Patch
@test/astro-markdown-skiki-themes-integrated Patch
@test/astro-markdown-skiki-wrap-false Patch
@test/astro-markdown-skiki-wrap-null Patch
@test/astro-markdown-skiki-wrap-true Patch
@test/astro-markdown-url Patch
@test/astro-markdown Patch
@test/astro-mode Patch
@test/astro-page-directory-url Patch
@test/astro-partial-html Patch
@test/astro-preview-allowed-hosts Patch
@test/astro-preview-headers Patch
@test/astro-public Patch
@test/astro-script-template-dedup Patch
@test/astro-scripts Patch
@test/astro-slots-nested Patch
@test/concurrency Patch
@test/build-readonly-file Patch
@test/cache-memory-query-include Patch
@test/cache-memory-query Patch
@test/client-address-node Patch
@test/client-only-css-chunk-leak Patch
@test/code-component Patch
@test/component-library Patch
@test/config-vite-css-target Patch
@test/config-vite Patch
@test/react-container Patch
@test/content-with-spaces-in-folder-name Patch
@test/content-collection-picture-render Patch
@example/content-collection-references Patch
@test/content-collection-tla-svg Patch
@test/content-collections-base Patch
@test/content-collections-empty-dir Patch
@test/content-collections-empty-md-file Patch
@test/content-collections-image-hmr Patch
@test/content-collections-mutation Patch
@test/content-collections-number-id Patch
@test/content-collections-type-inference Patch
@test/content-collections-with-config-mjs Patch
@test/content-collections Patch
@test/content-frontmatter Patch
@test/content-intellisense Patch
@test/content-layer-loader-schema-function Patch
@test/content-layer-remark-plugins Patch
@test/content-layer Patch
@test/content-ssr-integration Patch
@test/content-static-paths-integration Patch
@test/content Patch
@test/core-image-data-url Patch
@test/core-image-deletion-ssr Patch
@test/core-image-deletion Patch
@test/core-image-errors Patch
@test/core-image-fs-config Patch
@test/core-image-remark-infersize Patch
@test/core-image-layout Patch
@test/core-image-picture-emit-file Patch
@test/core-image-remark-imgattr Patch
@test/core-image-ssg Patch
@test/core-image-ssr Patch
@test/core-image-svg-in-client Patch
@test/core-image-svg Patch
@test/core-image-unconventional-settings Patch
@test/core-image Patch
@test/csp-adapter Patch
@test/csp-fonts Patch
@test/csp Patch
@test/css-assets Patch
@test/css-dangling-references Patch
@test/css-deduplication Patch
@test/css-double-bundle Patch
@test/css-dynamic-import-dev Patch
@test/css-import-as-inline Patch
@test/css-inline-stylesheets Patch
@test/css-no-code-split Patch
@test/css-path-case Patch
@test/css-pure-chunk-query-params Patch
@test/custom-404-injected-from-dep Patch
@test/custom-404-pkg Patch
custom-fetch-error-pages Patch
@test/custom-renderer Patch
@test/data-collections-schema Patch
@test/data-collections Patch
@test/debug-component Patch
@test/dev-container Patch
@test/dev-render Patch
@test/dev-request-url Patch
@test/dynamic-endpoint-collision Patch
@test/dynamic-route-build-file Patch
@test/endpoint-routing Patch
@test/error-bad-js Patch
@test/error-build-location Patch
@test/error-non-error Patch
@test/extension-matching Patch
@test/fetch Patch
@test/fonts Patch
@test/astro-fontsource-package Patch
@test/get-static-paths-pages Patch
@test/glob-pages-css Patch
@test/head-propagation-prerender-env Patch
@test/hmr-markdown Patch
@test/hmr-new-page Patch
@test/hmr-slots-render Patch
@test/hoisted-imports Patch
@test/html-component Patch
@test/html-escape Patch
@test/html-page Patch
@test/html-slots Patch
@test/hydration-race Patch
@test/i18n-client-import Patch
@test/i18n-css-leak-basic Patch
@test/import-ts-with-js Patch
@test/impostor-md-file Patch
@test/integration-add-page-extension Patch
@test/integration-server-setup Patch
@test/jsx-queue-rendering Patch
@test/large-array-solid Patch
@test/legacy-collections-backwards-compat Patch
@test/lightningcss-scoped-nesting Patch
@test/live-loaders Patch
@test/markdown Patch
@test/middleware-dev Patch
@test/middleware-full-ssr Patch
@test/middleware-no-user-middlewaqre Patch
@test/middleware-tailwind Patch
@test/minification-html-jsx Patch
@test/minification-html Patch
@test/non-ascii-path Patch
@test/non-html-pages Patch
@test/page-format Patch
@test/page-level-styles Patch
@test/parallel-components Patch
@test/partials-css-boundary Patch
@test/partials Patch
@test/passthrough-image-service Patch
@test/postcss Patch
@test/preact-compat-component Patch
@test/preact-component Patch
@test/remote-css Patch
@test/request-signal Patch
@test/reuse-injected-entrypoint Patch
@test/root-srcdir-css Patch
@test/scoped-style-strategy Patch
@test/server-entry-fake-adapter Patch
@test/server-entry Patch
@test/server-islands-hybrid Patch
@test/server-islands-ssr Patch
@test/sessions Patch
@test/slots-preact Patch
@test/slots-react Patch
@test/slots-solid Patch
@test/slots-svelte Patch
@test/slots-vue Patch
@test/solid-component Patch
@test/sourcemap Patch
@test/space-in-folder-name Patch
@test/special-chars-in-component-imports Patch
@test/ssr-assets Patch
@test/ssr-dynamic Patch
@test/ssr-partytown Patch
@test/ssr-prerender-get-static-paths Patch
@test/ssr-prerender Patch
@test/ssr-preview Patch
@test/ssr-renderers-static-vue Patch
@test/ssr-request Patch
@test/ssr-hoisted-script Patch
@test/ssr-scripts Patch
@test/static-build-code-component Patch
@test/static-build-dir Patch
@test/static-build-frameworks Patch
@test/static-build-page-url-format Patch
@test/static-build-ssr Patch
@test/static-build Patch
@test/static-redirect Patch
@test/svelte-component Patch
@test/svg-deduplication Patch
@test/tailwindcss Patch
@e2e/third-party-astro Patch
@test/url-import-suffix Patch
@test/view-transitions Patch
@test/virtual-astro-file Patch
@test/vitest Patch
@test/vue-component Patch
@test/vue-with-multi-renderer Patch
@test/alpinejs-basics Patch
@test/alpinejs-directive Patch
@test/alpinejs-plugin-script-import Patch
@test/astro-cloudflare-allowed-hosts Patch
@test/astro-cloudflare-astro-dev-platform Patch
@test/astro-cloudflare-astro-env Patch
@test/astro-cloudflare-binding-image-service Patch
@test/astro-cloudflare-cache-provider-wait-until Patch
@test/astro-cloudflare-client-address Patch
@test/astro-cloudflare-compile-image-service Patch
@test/astro-cloudflare-custom-entryfile Patch
@test/astro-cloudflare-dev-image-endpoint Patch
@test/astro-cloudflare-external-image-service Patch
@test/astro-cloudflare-external-redirects Patch
@test/astro-cloudflare-internal-redirects Patch
@test/astro-cloudflare-no-output Patch
@test/astro-cloudflare-prerender-node-env Patch
@test/astro-cloudflare-prerender-queue-consumers Patch
@test/astro-cloudflare-prerender-styles Patch
@test/astro-cloudflare-prerenderer-errors Patch
@test/routing-priority-cloudflare Patch
@test/cf-server-entry Patch
@test/astro-cloudflare-server-island-prerender-framework Patch
@test/astro-cloudflare-sql-import Patch
@test/cf-ssr-deps Patch
@test/astro-cloudflare-static Patch
@test/astro-cloudflare-svelte-rune-deps Patch
@test/astro-cloudflare-top-level-return Patch
@test/cf-user-optimize-deps Patch
@test/astro-cloudflare-vite-plugin Patch
@test/astro-cloudflare-with-base Patch
@test/astro-cloudflare-with-react Patch
@test/astro-cloudflare-with-solid-js Patch
@test/astro-cloudflare-with-svelte Patch
@test/astro-cloudflare-with-vue Patch
@test/astro-cloudflare-wrangler-preview-platform Patch
@test/markdoc-content-collections Patch
@test/content-layer-markdoc Patch
@test/headings-custom Patch
@test/headings Patch
@test/image-assets-custom Patch
@test/image-assets Patch
@test/markdoc-propagated-assets Patch
@test/markdoc-render-with-space Patch
@test/markdoc-render-html Patch
@test/markdoc-render-null Patch
@test/markdoc-render-partials Patch
@test/markdoc-render-simple Patch
@test/markdoc-render-table-attrs Patch
@test/markdoc-render-typographer Patch
@test/markdoc-render-with-components Patch
@test/markdoc-render-with-config Patch
@test/markdoc-render-with-extends-components Patch
@test/markdoc-render-with-indented-components Patch
@test/markdoc-render-with-transform Patch
@test/markdoc-variables Patch
@test/content-layer-rendering Patch
@test/mdx-css-head-mdx Patch
@test/image-remark-imgattr Patch
@test/mdx-astro-container-escape Patch
@test/mdx-frontmatter-injection Patch
@test/netlify-skew-protection Patch
@test/netlify-hosted-astro-project Patch
@test/nodejs-api-route Patch
@test/nodejs-badurls Patch
@test/nodejs-encoded Patch
@test/nodejs-errors Patch
@test/nodejs-headers Patch
@test/nodejs-image Patch
@test/locals Patch
@test/node-middleware Patch
@test/nodejs-prerender-404-500 Patch
@test/nodejs-prerender Patch
@test/nodejs-prerendered-error-page-fetch Patch
@test/nodejs-preview-headers Patch
@test/redirects Patch
@test/node-sessions Patch
@test/ssr-assets-middleware Patch
@test/node-static-headers Patch
@test/node-trailingslash Patch
@test/url Patch
@test/well-known-locations Patch
@test/react-component Patch
@test/sitemap-chunks Patch
@test/sitemap-dynamic Patch
@test/sitemap-i18n-fallback Patch
@test/sitemap-ssr Patch
@test/sitemap-static Patch
@test/sitemap-trailing-slash Patch
async-rendering Patch
conditional-rendering Patch
@test/empty-class Patch
svelte-prop-types Patch
@test/astro-vercel-basic Patch
@test/astro-vercel-image Patch
@test/astro-vercel-integration-assets Patch
@test/vercel-isr Patch
@test/vercel-max-duration Patch
@test/vercel-edge-middleware-with-edge-file Patch
@test/vercel-edge-middleware-without-edge-file Patch
@test/astro-vercel-no-output Patch
@test/astro-vercel-prerendered-error-pages Patch
@test/astro-vercel-redirects-serverless Patch
@test/astro-vercel-redirects Patch
@test/vercel-server-islands Patch
@test/astro-vercel-serverless-prerender Patch
@test/astro-vercel-serverless-with-dynamic-routes Patch
@test/astro-vercel-static-assets Patch
@test/vercel-static-headers Patch
@test/astro-vercel-static Patch
@test/vercel-streaming Patch
@test/astro-vercel-with-web-analytics-enabled-output-as-static Patch
vercel-hosted-astro-project Patch
@test/vue-app-entrypoint-async Patch
@test/vue-app-entrypoint-css Patch
@test/vue-app-entrypoint-no-export-default Patch
@test/vue-app-entrypoint-relative Patch
@test/vue-app-entrypoint-src-absolute Patch
@test/vue-app-entrypoint Patch
@test/vue-basics Patch
vue-prop-types Patch
astro-benchmark Patch
@benchmark/adapter Patch
@benchmark/timer Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added the pkg: astro Related to the core `astro` package (scope) label Jun 10, 2026
@codspeed-hq

codspeed-hq Bot commented Jun 10, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 18 untouched benchmarks


Comparing iseraph-dev:fix/advanced-routing-500-fallback (00f9f9f) with main (6c6c974)1

Open in CodSpeed

Footnotes

  1. No successful run was found on main (0d862d5) during the generation of this report, so 6c6c974 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report.

@iseraph-dev
iseraph-dev marked this pull request as ready for review June 11, 2026 07:34
// so routeData is only missing when the custom 404 page is
// prerendered (or absent) — let the error handler serve the
// pre-built page instead.
if (!state.routeData) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see why this is needed. The problem is 500s I thought? 404s are already handled a level up (in App that calls into the fetch handler).

@iseraph-dev iseraph-dev Jun 11, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This covers the case where the custom 404 is prerendered (or missing) - since #16911 routeData stays unset there, and pages() has no AstroHandler above it to turn that into a 404.

That said, I rebased on latest main and reworked this branch to return the X-Astro-Error response instead (same thing handle() does for the un-dispatched redirect case), so it's now handled by App's post-check a level up - which I think is the shape you were after. The 500 path still calls renderError directly, since the post-check can't carry the error object and 500.astro would lose its error prop. Tests still pass, I tightened the prerendered-404 one to assert the marker.

@iseraph-dev
iseraph-dev force-pushed the fix/advanced-routing-500-fallback branch from cb2f1b3 to 3ca4beb Compare June 12, 2026 11:17
…uting astro/hono handlers

The composable `astro/fetch` `pages()` entry point dispatched to
`PagesHandler.handle()` without any error handling, so a page that threw
during render propagated the error to the host framework (Hono returned
its default plain-text "Internal Server Error") instead of rendering the
custom `500.astro` page like the standard `AstroHandler` path does.

`pages()` now delegates to a new `PagesHandler.handleWithErrorFallback()`
that mirrors `AstroHandler`'s app-level error handling: unmatched routes
(possible when the custom 404 page is prerendered, see withastro#16911) render
the 404 error page, and render-time errors are logged and render the
500 error page via `app.renderError()` (loop-safe; falls back to a
plain response when the error page itself fails).

Fixes withastro#16952
Review feedback: instead of calling `app.renderError()` from
`handleWithErrorFallback()` when no route matched, return the
`X-Astro-Error`-marked 404 response (the same pattern `handle()` uses
for the un-dispatched redirect case) and let the app's existing
post-check render the 404 error page a level up. The 500 path keeps
calling `renderError()` directly because the marker cannot carry the
error object: the post-check passes `error: null`, which would drop the
`error` prop on `500.astro` and skip logging the stack.
The changeset only described the 500 behavior. The PR also changes what
happens when no route matches and the custom 404 page is prerendered
(or absent): previously the TypeError escaped to the host framework's
default error response, now the 404 error page is rendered.
@iseraph-dev

Copy link
Copy Markdown
Contributor Author

Picked this back up on the v7 beta. Advanced routing is unflagged now but the bug's still there, so I rebased onto main. The fix didn't need any changes - I just reworded the changeset since it's not experimental anymore.

@iseraph-dev
iseraph-dev force-pushed the fix/advanced-routing-500-fallback branch from c83263c to 00f9f9f Compare June 16, 2026 12:19
@matthewp
matthewp merged commit 4c4a91c into withastro:main Jun 16, 2026
29 checks passed
matthewp pushed a commit that referenced this pull request Jun 16, 2026
…ware() path (#17097)

On the composable path, middleware() had no app-level error handling. A throw in user middleware
  escaped to the host framework instead of rendering 500.astro, and an unmatched request (custom 404
  prerendered or absent) threw on the missing route instead of rendering 404.astro.

Add AstroMiddleware.handleWithErrorFallback mirroring PagesHandler.handleWithErrorFallback
  (#17041): a routeData guard returns a marked 404 for the app's post-check, and a sentinel-wrapped
  try/catch renders 500.astro for Astro middleware throws while re-throwing errors from next so host
  errors still reach app.onError.
@astrobot-houston astrobot-houston mentioned this pull request Jun 19, 2026
dadezzz pushed a commit to dadezzz/university_notes that referenced this pull request Jun 26, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [astro](https://astro.build) ([source](https://github.com/withastro/astro/tree/HEAD/packages/astro)) | [`6.4.8` → `7.0.0`](https://renovatebot.com/diffs/npm/astro/6.4.8/7.0.0) | ![age](https://developer.mend.io/api/mc/badges/age/npm/astro/7.0.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/astro/6.4.8/7.0.0?slim=true) |

---

### Release Notes

<details>
<summary>withastro/astro (astro)</summary>

### [`v7.0.0`](https://github.com/withastro/astro/blob/HEAD/packages/astro/CHANGELOG.md#700)

[Compare Source](https://github.com/withastro/astro/compare/[email protected]@7.0.0)

##### Major Changes

- [#&#8203;15819](withastro/astro#15819) [`cafec4e`](withastro/astro@cafec4e) Thanks [@&#8203;delucis](https://github.com/delucis)! - Upgrade to Vite v8

- [#&#8203;16965](withastro/astro#16965) [`57ead0d`](withastro/astro@57ead0d) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Makes `'jsx'` the default value for `compressHTML`

  Astro now strips whitespace from your HTML using JSX rules by default, the same way frameworks like React do. Whitespace and line breaks around elements are removed, but meaningful whitespace within a single line — like a space between two inline elements — is preserved. To keep a space that would otherwise be removed, write it explicitly in your source, for example with `{" "}`.

  This can change rendered output where whitespace between inline elements was previously meaningful. To keep Astro's earlier behavior, set `compressHTML: true` for HTML-aware compression, or `compressHTML: false` to preserve all whitespace.

- [#&#8203;16610](withastro/astro#16610) [`c63e7e4`](withastro/astro@c63e7e4) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Adds background dev server management for AI coding agents.

  When an AI coding agent is detected, `astro dev` now automatically starts the dev server as a detached background process. This prevents the dev server from blocking the agent's terminal and allows it to continue working while the server runs.

  A lock file (`.astro/dev.json`) is written when the dev server starts, recording the server's URL, port, and PID. This prevents duplicate servers from being started for the same project.

##### New flag and subcommands

- `astro dev --background` — Start the dev server as a background process (this is what runs automatically when an agent is detected).
- `astro dev stop` — Stop a running background dev server.
- `astro dev status` — Check if a dev server is running and display its URL, PID, and uptime.
- `astro dev logs` — View logs from a background dev server. Use `--follow` (`-f`) to stream new output as it's written.

These allow you to start and manage dev servers programmatically and were designed with AI coding agents in mind.

##### What should I do?

No action is required. If you are not using an AI coding agent, `astro dev` behaves exactly as before. If you are using an agent, background mode is enabled automatically — the agent will receive the server URL and PID, and can use `astro dev stop` to shut it down.

To opt out of automatic background mode when an agent is detected, set the environment variable `ASTRO_DEV_BACKGROUND=0` before running `astro dev`.

- [#&#8203;17010](withastro/astro#17010) [`0606073`](withastro/astro@0606073) Thanks [@&#8203;ocavue](https://github.com/ocavue)! - Removes the `@astrojs/db` package as it is no longer maintained.

  The `@astrojs/db` package were deprecated in v6.4.5 and is now removed. This means the `astro db`, `astro login`, `astro logout`, `astro link`, and `astro init` CLI commands have also been removed.

  If you were using Astro DB in your project, remove `@astrojs/db` from your project's dependencies and replace it with one of the following alternatives:

  - **Node.js built-in SQLite**: Node.js now includes a built-in [`node:sqlite`](https://nodejs.org/api/sqlite.html) module (available since Node.js v22.5.0). This is a good option if you are using the Node.js adapter and were using `@astrojs/db` for local SQLite storage.
  - **[Drizzle ORM](https://orm.drizzle.team/)**: If you were using `@astrojs/db` for its Drizzle-based schema and query API, you can use Drizzle directly with any supported database.
  - **Other database libraries**: Use any database library that suits your deployment platform (e.g. [Turso](https://turso.tech/), [PlanetScale](https://planetscale.com/), [Neon](https://neon.tech/)).

- [#&#8203;16462](withastro/astro#16462) [`c30a778`](withastro/astro@c30a778) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Replaces the Go compiler with a Rust-based version.

  The Rust-based Astro compiler (`@astrojs/compiler-rs`) is now the default compiler. This new compiler is faster and more reliable, leading to faster build times and iteration cycles during development.

  This new compiler is more strict regarding invalid syntax. For example, unclosed HTML tags will now throw an error instead of being ignored. It also does not attempt to correct semantically invalid HTML anymore, instead leaving it to the browser to handle, similar to other tools or `document.write()` in JavaScript.

  The previous Go-based compiler has been removed, along with the `experimental.rustCompiler` flag used to opt into the Rust compiler. If you were setting `experimental.rustCompiler` in your `astro.config.mjs`, you can now remove it. No other action is required.

- [#&#8203;16966](withastro/astro#16966) [`6650ec2`](withastro/astro@6650ec2) Thanks [@&#8203;Princesseuh](https://github.com/Princesseuh)! - Makes Sätteri the default Markdown processor

  Astro now renders `.md` files with `satteri()` from `@astrojs/markdown-satteri`, its native Markdown pipeline, instead of the remark/rehype pipeline. `@astrojs/markdown-remark` is no longer installed by default.

  To keep using the remark/rehype pipeline, install `@astrojs/markdown-remark` and set it as your processor:

  ```js
  // astro.config.mjs
  import { defineConfig } from 'astro/config';
  import { unified } from '@&#8203;astrojs/markdown-remark';

  export default defineConfig({
    markdown: {
      processor: unified(),
    },
  });
  ```

  The deprecated `markdown.remarkPlugins`, `markdown.rehypePlugins`, and `markdown.remarkRehype` options still work, but now require `@astrojs/markdown-remark` to be used.

- [#&#8203;16877](withastro/astro#16877) [`3b7d76e`](withastro/astro@3b7d76e) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Enables advanced routing by default.

  The advanced routing feature introduced behind a flag in [v6.3.0](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md#630) is no longer experimental and is now enabled by default.

  This gives full control over how requests flow through your application, with first-class support for frameworks like Hono.

  Advanced routing now uses `src/fetch.ts` as default entrypoint instead of `src/app.ts`.

  If you were previously using this feature without a custom entrypoint, please configure `fetchFile` or rename your entrypoint to `src/fetch.ts`, and then remove the experimental flag from your Astro config:

  ```diff
  import { defineConfig } from 'astro/config';

  export default defineConfig({
    experimental {
  -    advancedRouting: true,
    },
  +  fetchFile: 'app.ts' // optional, you only need this if you cannot rename your entrypoint.
  });
  ```

  `fetchFile` is now a top-level config option instead of being nested under `experimental.advancedRouting`. If you were using a custom entrypoint, please update your Astro config to move its configuration:

  ```diff
  // astro.config.mjs
  export default defineConfig({
  -  experimental: {
  -    advancedRouting: {
  -      fetchFile: 'my-custom-entrypoint.ts',
  -    },
  -  },
  +  fetchFile: 'my-custom-entrypoint.ts',
  })
  ```

  You can also set `fetchFile: null` to disable the entrypoint if you are using `src/fetch.ts` for another purpose, or don’t need advanced routing features.

  If you have been waiting for stabilization before using advanced routing, you can now do so.

  Please see [the advanced routing guide in docs](https://docs.astro.build/en/guides/routing/#advanced-routing) for more about this feature.

- [#&#8203;16725](withastro/astro#16725) [`10229f7`](withastro/astro@10229f7) Thanks [@&#8203;ArmandPhilippot](https://github.com/ArmandPhilippot)! - Removes deprecated APIs exported from `astro:transitions`.

  In Astro 6.x, some helpers available in `astro:transitions` and `astro:transitions/client` were deprecated.

  In Astro 7.0, the following APIs can no longer be used in your project:

  - `TRANSITION_BEFORE_PREPARATION`
  - `TRANSITION_AFTER_PREPARATION`
  - `TRANSITION_BEFORE_SWAP`
  - `TRANSITION_AFTER_SWAP`
  - `TRANSITION_PAGE_LOAD`
  - `isTransitionBeforePreparationEvent()`
  - `isTransitionBeforeSwapEvent()`
  - `createAnimationScope()`

##### What should I do?

Remove any occurrence of `createAnimationScope()`:

```diff
-import { createAnimationScope } from 'astro:transitions';
```

Replace any occurrence of the other APIs using the lifecycle event names directly:

```diff
-import {
-	TRANSITION_AFTER_SWAP,
-	isTransitionBeforePreparationEvent,
-} from 'astro:transitions/client';

-console.log(isTransitionBeforePreparationEvent(event));
+console.log(event.type === 'astro:before-preparation');

-console.log(TRANSITION_AFTER_SWAP);
+console.log('astro:after-swap');
```

Learn more about all utilities available in the [View Transitions Router API Reference](https://docs.astro.build/en/reference/modules/astro-transitions/).

##### Minor Changes

- [#&#8203;16998](withastro/astro#16998) [`57dcc31`](withastro/astro@57dcc31) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Exposes `getFetchState()` from `astro/hono` as a public API

  The `getFetchState()` function retrieves or lazily creates a `FetchState` from a Hono context object. This allows third-party packages to build Hono middleware that interacts with Astro's per-request state, giving the `astro/hono` API the same extensibility as `astro/fetch`.

  ```ts
  import { Hono } from 'hono';
  import { getFetchState, pages } from 'astro/hono';

  const app = new Hono();

  app.use(async (context, next) => {
    const state = getFetchState(context);
    state.locals.message = 'Hello from custom middleware';
    await next();
  });

  app.use(pages());

  export default app;
  ```

- [#&#8203;16996](withastro/astro#16996) [`300641e`](withastro/astro@300641e) Thanks [@&#8203;florian-lefebvre](https://github.com/florian-lefebvre)! - Adds a `subset` field to the `FontData` type exposed via `fontData` from `astro:assets`. When using multiple font subsets (e.g., `subsets: ["latin", "korean"]`), each font data entry now includes the subset name, making it possible to distinguish between font entries for different subsets that share the same weight and style.

- [#&#8203;16745](withastro/astro#16745) [`f864a80`](withastro/astro@f864a80) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - The custom logger feature introduced behind a flag in [v6.2.0](https://github.com/withastro/astro/blob/main/packages/astro/CHANGELOG.md#620) is no longer experimental and is available for general use.

  This feature provides better control over Astro's logging infrastructure by allowing you to replace the default console output with custom logging implementations (e.g., structured JSON). This is particularly useful for on-demand rendering when connecting to log aggregation services such as Kibana, Logstash, CloudWatch, Grafana, or Loki.

  Astro provides three built-in log handlers (`json`, `node`, and `console`), and you can also create your own.

##### JSON logging

```js
import { defineConfig, logHandlers } from 'astro/config';

export default defineConfig({
  logger: logHandlers.json({
    pretty: true,
    level: 'warn',
  }),
});
```

##### Custom logger

```js
import { defineConfig } from 'astro/config';

export default defineConfig({
  logger: {
    entrypoint: '@&#8203;org/custom-logger',
  },
});
```

Additionally, `context.logger` is now always available in API routes and middleware, even without a custom logger configured.

If you were previously using this feature, please remove the experimental flag from your Astro config:

```diff
import { defineConfig } from 'astro/config';

export default defineConfig({
-  experimental: {
-    logger: {
-      entrypoint: '@&#8203;org/custom-logger',
-    },
-  },
+  logger: {
+    entrypoint: '@&#8203;org/custom-logger',
+  },
});
```

If you have been waiting for stabilization before using custom loggers, you can now do so.

Please see the [Logger docs](https://docs.astro.build/en/reference/configuration-reference/#logger) for more about this feature.

- [#&#8203;16981](withastro/astro#16981) [`0d6d644`](withastro/astro@0d6d644) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Removes the setting `experimental.queuedRendering`. The new rendering engine is now stable and replaces the old one.

  As part of the stabilization, the queued rendering has been improved, and some features have been removed:

  - The construction of the queue has been removed, instead now Astro uses a streaming approach where components are rendered and flushed as they are encountered.
  - The node polling feature has been removed because it doesn't yield concrete savings.
  - The content cache has been descoped, and how only tag names are cached.
    If you were previously using this experimental feature, you must remove this experimental flag from your configuration as it no longer exists:

  ```diff
  // astro.config.mjs
  import { defineConfig } from "astro/config";

  export default defineConfig({
    experimental: {
  -    queuedRendering: {}
    }
  });
  ```

- [#&#8203;17116](withastro/astro#17116) [`f95e58e`](withastro/astro@f95e58e) Thanks [@&#8203;ascorbic](https://github.com/ascorbic)! - Stabilizes route caching, removing the `experimental.cache` and `experimental.routeRules` flags and replacing them with the top-level `cache` and `routeRules` configuration options.

  Route caching, introduced experimentally in v6.0.0, is now stable. It gives you a platform-agnostic way to cache responses from [on-demand rendered](https://docs.astro.build/en/guides/on-demand-rendering/) pages and endpoints, based on standard HTTP caching semantics.

  Update your config to move `cache` and `routeRules` out of the `experimental` block:

  ```diff
  // astro.config.mjs
  import { defineConfig, memoryCache } from 'astro/config';

  export default defineConfig({
  -  experimental: {
  -    cache: {
  -      provider: memoryCache(),
  -    },
  -    routeRules: {
  -      '/blog/[...path]': { maxAge: 300, swr: 60 },
  -    },
  -  },
  +  cache: {
  +    provider: memoryCache(),
  +  },
  +  routeRules: {
  +    '/blog/[...path]': { maxAge: 300, swr: 60 },
  +  },
  });
  ```

  Set caching directives in your routes with `Astro.cache` (in `.astro` pages) or `context.cache` (in API routes and middleware), and Astro translates them into the appropriate headers or runtime behavior depending on your configured cache provider. You can also define cache rules for routes declaratively in your config using `routeRules`, without modifying route code.

  See the [route caching guide](https://docs.astro.build/en/guides/caching/) for more information.

##### Patch Changes

- [#&#8203;16980](withastro/astro#16980) [`1f07343`](withastro/astro@1f07343) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Removes `state.provide()`, `state.resolve()`, `state.finalizeAll()`, and `App.Providers` from the public advanced routing API. These context provider extension points are now internal-only. If you were using them in an integration, use `locals` to share per-request state instead.

- [#&#8203;17111](withastro/astro#17111) [`c0f33ed`](withastro/astro@c0f33ed) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Harden the limits on the number of decoding on the URL.

- [#&#8203;16982](withastro/astro#16982) [`1e000e2`](withastro/astro@1e000e2) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Improves the warning when accessing `Astro.session` without session storage configured. The `session` property is now always defined on the context object, and accessing it without configuration logs a helpful message instead of silently returning `undefined`.

- [#&#8203;16335](withastro/astro#16335) [`9a53f77`](withastro/astro@9a53f77) Thanks [@&#8203;ascorbic](https://github.com/ascorbic)! - Adds shared helper utilities for CDN cache provider authors for [route caching](https://docs.astro.build/en/guides/caching/)

  Exports `astro/cache/provider-utils` with helpers for building platform-specific cache-control headers, generating path-based invalidation tags, and normalizing invalidation options. These are used internally by the first-party Netlify, Vercel, and Cloudflare cache providers.

- [#&#8203;17095](withastro/astro#17095) [`e84ebc0`](withastro/astro@e84ebc0) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Improves build performance by removing an unfiltered transform hook from the `astro:head-metadata-build` plugin. Head propagation modules are now identified by their module ID (`?astroPropagatedAssets`) instead of scanning every module's source code.

- [#&#8203;17041](withastro/astro#17041) [`4c4a91c`](withastro/astro@4c4a91c) Thanks [@&#8203;iseraph-dev](https://github.com/iseraph-dev)! - Fixes a bug where the advanced routing `astro/hono` / `astro/fetch` `pages()` handler returned the host framework's default `Internal Server Error` response instead of rendering the custom `500.astro` page when a page threw during render. Unmatched requests with a prerendered (or absent) custom 404 page now render the 404 error page instead of failing the same way.

- [#&#8203;17097](withastro/astro#17097) [`5e340d7`](withastro/astro@5e340d7) Thanks [@&#8203;iseraph-dev](https://github.com/iseraph-dev)! - Fixes a bug where the advanced routing `astro/hono` / `astro/fetch` `middleware()` handler returned the host framework's default `Internal Server Error` response instead of rendering the custom `500.astro` page when middleware threw. Unmatched requests with a prerendered (or absent) custom 404 page now render the 404 error page instead of failing the same way. Errors surfaced through `next` (the host framework's downstream chain) still propagate to the host's own error handler.

- [#&#8203;15819](withastro/astro#15819) [`cafec4e`](withastro/astro@cafec4e) Thanks [@&#8203;delucis](https://github.com/delucis)! - Fixes `--port` flag being ignored after a Vite-triggered server restart (e.g. when a `.env` file changes)

- [#&#8203;17104](withastro/astro#17104) [`b074a37`](withastro/astro@b074a37) Thanks [@&#8203;iseraph-dev](https://github.com/iseraph-dev)! - Fixes the custom `500.astro` page receiving an empty `error` prop when the error originated in middleware.

- [#&#8203;17078](withastro/astro#17078) [`04547ec`](withastro/astro@04547ec) Thanks [@&#8203;astrobot-houston](https://github.com/astrobot-houston)! - Fixes a spurious `Astro.request.headers` warning on prerendered pages when `security.allowedDomains` is configured. The internal `allowedDomains` header validation now skips prerendered routes, since they use synthetic requests with no real headers.

- [#&#8203;16603](withastro/astro#16603) [`deaaf3f`](withastro/astro@deaaf3f) Thanks [@&#8203;alexanderniebuhr](https://github.com/alexanderniebuhr)! - Removes the warning that Astro does not support vite v8, since Astro v7 does support vite v8

- [#&#8203;16335](withastro/astro#16335) [`9a53f77`](withastro/astro@9a53f77) Thanks [@&#8203;ascorbic](https://github.com/ascorbic)! - Passes the `Request` object to `CacheProvider.setHeaders()` for [route caching](https://docs.astro.build/en/guides/caching/)

  Cache providers now receive the incoming `Request` as a second argument to `setHeaders(options, request)`. This allows CDN providers to read the request URL, headers, and other properties when generating cache response headers, for example to auto-tag responses with their pathname for path-based invalidation.

- [#&#8203;17098](withastro/astro#17098) [`637a1b6`](withastro/astro@637a1b6) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes internal Astro headers leaking from direct `pages()` handler responses

- [#&#8203;17090](withastro/astro#17090) [`3cf76c0`](withastro/astro@3cf76c0) Thanks [@&#8203;matthewp](https://github.com/matthewp)! - Fixes Vite and Rolldown build warnings

- [#&#8203;16434](withastro/astro#16434) [`ee079d4`](withastro/astro@ee079d4) Thanks [@&#8203;ematipico](https://github.com/ematipico)! - Fixes an issue where i18n domains would return 404 when `trailingSlash` is set to `never`.

- Updated dependencies \[[`7e7ab87`](withastro/astro@7e7ab87), [`ff7b718`](withastro/astro@ff7b718), [`241250b`](withastro/astro@241250b)]:
  - [@&#8203;astrojs/markdown-satteri](https://github.com/astrojs/markdown-satteri)@&#8203;0.3.1

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](undefined) if that's undesired.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMzQuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIzNC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pkg: astro Related to the core `astro` package (scope)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Custom 500.astro not rendered for render-time errors under experimental.advancedRouting + astro/hono pages()

3 participants