Skip to content

fix(nuxt): skip plugin initialization during server-side error render#35320

Draft
stephanelgrg wants to merge 2 commits into
nuxt:mainfrom
stephanelgrg:fix/issue-20909-plugin-ssr-error-recursion
Draft

fix(nuxt): skip plugin initialization during server-side error render#35320
stephanelgrg wants to merge 2 commits into
nuxt:mainfrom
stephanelgrg:fix/issue-20909-plugin-ssr-error-recursion

Conversation

@stephanelgrg

Copy link
Copy Markdown

🔗 Linked issue

Resolves #20909

📚 Description

When a server-only plugin calls $fetch() for a server route and that route throws, Nuxt would still initialize plugins during the server error render. That caused the plugin to run again while rendering the error response, leading to an infinite recursion loop.

Fix

  • Detect server-side error renders in entry.ts.

  • Skip applyPlugins(nuxt, plugins) when ssrContext.payload.error or ssrContext.error is present

  • Preserve normal hook invocation and error handling for error rendering

@stephanelgrg stephanelgrg requested a review from danielroe as a code owner June 9, 2026 21:14
@github-actions github-actions Bot added 5.x 🐛 bug Something isn't working as expected labels Jun 9, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jun 9, 2026

Copy link
Copy Markdown

Open in StackBlitz

@nuxt/kit

npm i https://pkg.pr.new/@nuxt/kit@35320

@nuxt/nitro-server

npm i https://pkg.pr.new/@nuxt/nitro-server@35320

nuxt

npm i https://pkg.pr.new/nuxt@35320

@nuxt/rspack-builder

npm i https://pkg.pr.new/@nuxt/rspack-builder@35320

@nuxt/schema

npm i https://pkg.pr.new/@nuxt/schema@35320

@nuxt/vite-builder

npm i https://pkg.pr.new/@nuxt/vite-builder@35320

@nuxt/webpack-builder

npm i https://pkg.pr.new/@nuxt/webpack-builder@35320

commit: f8b4f88

@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: ea93c5e4-c159-4653-9334-1435a7102af5

📥 Commits

Reviewing files that changed from the base of the PR and between 7d2e635 and f8b4f88.

📒 Files selected for processing (2)
  • packages/nuxt/src/app/entry.ts
  • test/bundle.test.ts
✅ Files skipped from review due to trivial changes (1)
  • test/bundle.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/nuxt/src/app/entry.ts

Walkthrough

This pull request modifies the server-side entry point to detect and handle error-render scenarios. When the server context contains an existing error (via ssrContext.payload.error or ssrContext.error), the bootstrap process now skips the plugin application phase. This prevents redundant plugin execution during error responses, whilst preserving the app:created hook and existing error handling pathways that populate nuxt.payload.error.

Possibly related PRs

  • nuxt/nuxt#35316: Also modifies test/bundle.test.ts server bundle size inline snapshot expectations (adjusted serverStats.totalBytes), indicating related test expectation updates.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'fix(nuxt): skip plugin initialization during server-side error render' clearly summarizes the main change - skipping plugin initialization during error rendering on the server side.
Description check ✅ Passed The description is directly related to the changeset, explaining the problem, the fix, and the specific changes made to address the infinite recursion issue.
Linked Issues check ✅ Passed The pull request addresses all coding requirements from issue #20909: detects server-side error renders, skips applyPlugins when errors are present, and preserves hook invocation and error handling.
Out of Scope Changes check ✅ Passed All changes are directly related to the linked issue: the entry.ts modification implements the fix, and the test snapshot update reflects the minor impact of that fix.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/nuxt/src/app/entry.ts`:
- Around line 40-42: The current early-return when isErrorRender prevents
applyPlugins(nuxt, plugins) from running and breaks router/link-dependent error
pages; ensure plugin initialization still runs during SSR error render by
removing or altering the isErrorRender guard so applyPlugins(nuxt, plugins) is
always invoked (or at minimum ensure the router plugin and any plugins that
register nuxtApp.hooks.hookOnce('app:created', ...) and provide $router are
executed before error rendering); locate the conditional around applyPlugins in
entry.ts and either move applyPlugins outside the isErrorRender branch or
explicitly call the router-related plugin setup prior to rendering errors so
useRouter() and NuxtLink remain available.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f4cc0093-dcec-43a3-8341-6dedb72cabf1

📥 Commits

Reviewing files that changed from the base of the PR and between 95ab040 and 7d2e635.

📒 Files selected for processing (1)
  • packages/nuxt/src/app/entry.ts

Comment on lines +40 to +42
if (!isErrorRender) {
await applyPlugins(nuxt, plugins)
}

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check if error pages depend on plugin-provided features

# Find error page components and check for router/nuxt-link usage
echo "=== Checking error pages for router/link usage ==="
fd -e vue -e ts -e js --full-path 'error' --exec rg -l 'NuxtLink|useRouter|useRoute' {}

# Check for plugin files that register critical hooks
echo -e "\n=== Checking plugins for app:created hooks ==="
fd -e ts -e js --full-path 'plugins' --exec rg -n "hooks\.(hook|hookOnce)\s*\(\s*['\"]app:created" {}

# Look for server-only plugins that might be the source of recursion
echo -e "\n=== Finding server-only plugins ==="
fd '\.server\.(ts|js)$' --full-path 'plugins'

Repository: nuxt/nuxt

Length of output: 594


Avoid breaking SSR error rendering by skipping all plugins
Skipping applyPlugins(nuxt, plugins) during SSR error render prevents plugin-provided setup from running—particularly the router plugin logic that registers nuxtApp.hooks.hookOnce('app:created', ...) and provides $router. Error-page-related code already depends on router/link functionality (e.g. test/fixtures/basic/app/pages/navigate-to-validate-custom-error.vue uses NuxtLink/router APIs, and packages/nuxt/src/app/composables/error.ts uses useRouter()), so error rendering can fail or misbehave without plugin initialisation.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/nuxt/src/app/entry.ts` around lines 40 - 42, The current
early-return when isErrorRender prevents applyPlugins(nuxt, plugins) from
running and breaks router/link-dependent error pages; ensure plugin
initialization still runs during SSR error render by removing or altering the
isErrorRender guard so applyPlugins(nuxt, plugins) is always invoked (or at
minimum ensure the router plugin and any plugins that register
nuxtApp.hooks.hookOnce('app:created', ...) and provide $router are executed
before error rendering); locate the conditional around applyPlugins in entry.ts
and either move applyPlugins outside the isErrorRender branch or explicitly call
the router-related plugin setup prior to rendering errors so useRouter() and
NuxtLink remain available.

@codspeed-hq

codspeed-hq Bot commented Jun 9, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 20 untouched benchmarks
⏩ 3 skipped benchmarks1


Comparing stephanelgrg:fix/issue-20909-plugin-ssr-error-recursion (f8b4f88) with main (95ab040)

Open in CodSpeed

Footnotes

  1. 3 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@danielroe danielroe left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this would mean you couldn't access vue router, useRoute, etc., which are instantiated in plugins

in general this indicates a bug in user code - and we can't know for sure which plugin to skip, if any

@stephanelgrg

stephanelgrg commented Jun 9, 2026

Copy link
Copy Markdown
Author

Yes you are right, for the 1st run as there's no error yet all the plugins are going to run.
But for the 2nd iteration, this is going to skip all plugins. 😵‍💫

I'll think about it and keep you posted ✌️

@danielroe

Copy link
Copy Markdown
Member

I mean, we need probably need plugins to run to render the error page.

the ideal here would be to warn the user if this happens in development (with an explanation of which plugin, perhaps?) and ideally abort an infinite loop with a better error in production...

@danielroe danielroe marked this pull request as draft June 10, 2026 08:52
@stephanelgrg

Copy link
Copy Markdown
Author

💯 I'll check multiple things and come-back to you to know in which direction to go.

But what I've in mind is this:

  • My colleague, @PReynaud, suggested me to add a "skippable" option on a plugin, to let the devs decide when we can skip it on SSR error for example.
  • I started to think about the ability to skip a broken plugin automatically on SSR (I don't know yet if it's doable).
  • But for these 2 solutions I've the same problem to solve: what's going to happen to the next plugins to execute if they rely on the result of the skipped one... 😬

Maybe I should proceed by baby steps (to move forward) 🐾

  1. Just a warn in dev mode
    And skip all the plugins maybe? As we are in dev mode it will ease to locate the warn in the logs if there's no infinite loop (but provide a meaningful log / error).

  2. Then think about a solution for prod purpose?

This is so interesting, I love it! 🤩

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

5.x 🐛 bug Something isn't working as expected

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Throwing an error in a server route causes an infinite loop when fetched inside a server only plugin

2 participants