Postmortem·
Postmortem: 500 Internal Server Error on ISR Routes
Resolving Nuxt Content payload extraction issues and hydration crashes on ISR routes.
Summary
A 500 error occurred when reloading or navigating to ISR routes (e.g., /blog/**). The failure was caused by nuxtApp.payload.data being undefined during client-side hydration.
Impact
- Affected routes:
/blog/**and other ISR paths - Trigger: Page reload or SPA → ISR navigation
- Result: Runtime crash → 500 error
Root Cause
Conflicting route rules in the configuration:
'/**': { ssr: false } // SPA catch-all
'/blog/**': { isr: 3600 } // ISR requires SSR
- The SPA catch-all disabled SSR globally.
- ISR routes expected SSR + payload.
- Client hydration attempted to access the missing payload, leading to a crash (Figure 1).
Related upstream issue: https://github.com/nuxt/nuxt/issues/34856
Resolution
Option A: Official Recommended Solution (Applied)
- Explicit Route Rules: Remove the global SPA catch-all (
/**: { ssr: false }) which disables SSR globally. Instead, define explicit rendering modes for each route group innuxt.config.ts:routeRules: { '/app/**': { ssr: false }, '/dashboard/**': { ssr: false }, '/blog/**': { isr: 3600 } } - Inline Route Rules: Enable
experimental: { inlineRouteRules: true }to allow defining rules (likeprerender: truefor SSG) directly inside.vuepages using thedefineRouteRulesmacro. - Re-enable Payload Extraction: Keep
payloadExtraction: true(default) to ensure efficient data loading and caching.
Option B: Legacy "Trick" (Alternative)
If explicit routing is too complex, the following "trick" can bypass the hydration crash:
- Disable payload extraction:
experimental: { payloadExtraction: false } - Client-side safeguard:
// app/plugins/payload-fix.ts export default defineNuxtPlugin((nuxtApp) => { if (process.client && !nuxtApp.payload?.data) { nuxtApp.payload.data = reactive({}) } })
Trade-offs
A summary of the trade-offs between the two options is detailed in Table 1.
| Approach | Advantages | Disadvantages |
|---|---|---|
| Option A: Official Way | Cleanest architecture, fully supported by Nuxt, best performance. | Requires explicit maintenance of route patterns. |
| Option B: Legacy Trick | Quickest fix, bypasses hydration crashes without strict routing. | Inlines payloads in HTML (larger responses), bypasses standard Nuxt hydration logic. |
Table 1: Trade-offs between official and legacy solutions
Prevention / Next Steps
- Never use a global SPA (
ssr: false) fallback if your app contains ISR or SSG routes. - Use
defineRouteRulesin pages for granular control over pre-rendering. - Maintain explicit route groupings in
nuxt.config.tsfor large modules.
Comments
INCIDENT_REPORT
Postmortem: Nuxt Content 404s on Staging Deployments
Previous
Postmortem: Nuxt Content 404s on Staging Deployments
Debugging missing content and 404 errors when deploying Nuxt Content to Cloudflare D1.
INCIDENT_REPORT
Post-Mortem: Intrusive Database Driver Refactoring
Next
Post-Mortem: Intrusive Database Driver Refactoring
A post-mortem on handling architectural integrity when an AI assistant performs an unauthorized database driver refactoring.
