Summary
The production server's 404 path correctly looks up the user's 404.stx page (if present) via exactRoutes.get('/404'). The 500 path at packages/stx/src/production-server.ts:266 does NOT do the equivalent — it returns plain text:
return new Response('Internal Server Error', { status: 500 })A project that ships pages/500.stx for branded error UI still gets an unbranded "Internal Server Error" string when the server hits an exception.
Reproducer
my-app/
pages/
500.stx ← user's custom error page
stx.config.tsBuild and run via stx start. Force any server-side error (corrupted manifest entry, throw in route middleware, etc.). Response body is text/plain "Internal Server Error", not the rendered 500.stx.
Root cause
packages/stx/src/production-server.ts:228-239 — for 404:
const notFoundRoute = exactRoutes.get('/404')
if (notFoundRoute) {
const compiled = compiledTemplates.get(notFoundRoute.filePath)
if (compiled) {
return new Response(compiled.html, {
status: 404,
headers: { 'Content-Type': 'text/html; charset=utf-8' },
})
}
}
return new Response('Not Found', { status: 404 })But the corresponding fallback at line 266 has no such lookup — it skips straight to plain text.
Impact
- Branded 500 pages don't work in production.
- Inconsistency with the 404 path is confusing.
- Apps relying on
pages/500.stxfor legal/branding reasons silently regress on errors.
Suggested fix
Mirror the 404 pattern:
const errorRoute = exactRoutes.get('/500')
if (errorRoute) {
const compiled = compiledTemplates.get(errorRoute.filePath)
if (compiled) {
return new Response(compiled.html, {
status: 500,
headers: { 'Content-Type': 'text/html; charset=utf-8' },
})
}
}
return new Response('Internal Server Error', { status: 500 })Worth confirming with the same treatment whether any other error responses (502/503/504) should also support custom pages from the user's pages/ tree.
Environment
- stx main
- Bun 1.3.13