Skip to content

fix(actions): return 404 + x-nextjs-action-not-found for missing actions - #1386

Merged
james-elicx merged 1 commit into
mainfrom
fix/issue-1340-server-actions-errors
May 21, 2026
Merged

fix(actions): return 404 + x-nextjs-action-not-found for missing actions#1386
james-elicx merged 1 commit into
mainfrom
fix/issue-1340-server-actions-errors

Conversation

@james-elicx

Copy link
Copy Markdown
Member

Summary

Recognise the prod-mode error shape from @vitejs/plugin-rsc when a server-action
reference id is missing from the manifest, so apps return Next.js' standard
404 + x-nextjs-action-not-found: 1 response (with content-type: text/plain and
body Server action not found.) instead of a generic 500.

@vitejs/plugin-rsc raises two different "no such server reference" errors
depending on the build mode, and both mean the same thing:

  • dev: [vite-rsc] invalid server reference '<id>' (reference validation virtual)
  • prod: server reference not found '<id>' (virtual:vite-rsc/server-references lookup, including the no-server-actions-at-all case)

Only the dev-mode message was matched before, so production builds (and apps
with no server actions defined at all) fell through to the generic 500 path. The
progressive (no-JS) handler hits the same code path via decodeAction(body)
before it has resolved an action id, so the actionId: null branch was widened
to recognise both error patterns too.

This addresses sub-problem #1 from #1340 (404 + x-nextjs-action-not-found).
Two sub-problems remain as follow-ups:

Refs #1340

Test plan

  • Added unit test reproducing the prod-mode server reference not found '<id>' shape on the fetch-action path; confirmed it failed on main (500 received, 404 expected) and now passes.
  • Added matching unit test for the progressive (MPA) decode path with the prod error shape.
  • pnpm test tests/app-server-action-execution.test.ts — 37 passed.
  • pnpm run check — format + lint + types clean.
  • CI: Check, Vitest, Playwright E2E (this PR).

Treat both build-time error shapes raised by `@vitejs/plugin-rsc` when a
server-action reference is missing from the manifest — dev's
`[vite-rsc] invalid server reference '<id>'` and prod's
`server reference not found '<id>'` — as Next.js action-not-found.

Previously only the dev-mode message was recognised, so production
builds (and apps with no server actions defined at all) returned 500
instead of Next.js' 404 + `x-nextjs-action-not-found: 1` response.
Both the fetch-action and progressive (no-JS) code paths benefit, since
the latter calls `isServerActionNotFoundError(error, null)` before it
has resolved an action id.

Refs #1340
@pkg-pr-new

pkg-pr-new Bot commented May 20, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/vinext@1386

commit: 92cadc4

@github-actions

Copy link
Copy Markdown
Contributor
Example Preview Production Original
app-router-cloudflare preview production
pages-router-cloudflare preview production
app-router-playground preview production original
realworld-api-rest preview production
nextra-docs-template preview production
benchmarks preview production
hackernews preview production original
web preview production

@james-elicx

Copy link
Copy Markdown
Member Author

/bigbonk review

@ask-bonk ask-bonk 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.

Clean, focused fix. The before/after is clear:

Old behavior: isServerActionNotFoundError(error, null) early-returned on message.startsWith("Failed to find Server Action") — so only the vinext-generated prefix was matched. When actionId was non-null, only the dev-mode [vite-rsc] invalid server reference pattern was checked. The prod-mode server reference not found '<id>' shape was never recognised on either path, producing a 500 instead of the expected 404 + x-nextjs-action-not-found: 1.

New behavior: Both the actionId and !actionId branches now check both plugin error shapes (dev [vite-rsc] invalid server reference and prod server reference not found), so production builds and zero-server-action apps get the correct 404 response.

The code reads well — the actionId branch uses exact includes() with the id interpolated (no false positives from substring collisions), and the !actionId branch falls back to regex anchored on the distinctive prefix+quote. The comment block explaining the two error shapes and linking to the plugin source is thorough and will save future debuggers time.

Tests are well-structured: one for the fetch-action (RSC) path, one for the progressive (MPA) path, both asserting status, header, content-type, body text, and that reportRequestError is not called (since this is a controlled 404, not an internal error).

No concerns.

@ask-bonk

ask-bonk Bot commented May 20, 2026

Copy link
Copy Markdown
Contributor

Approved PR #1386. The fix correctly recognises the prod-mode server reference not found '<id>' error shape from @vitejs/plugin-rsc alongside the already-handled dev-mode [vite-rsc] invalid server reference '<id>' shape, ensuring both the fetch-action (RSC) and progressive (MPA) code paths return the standard Next.js 404 + x-nextjs-action-not-found: 1 response instead of a generic 500. Tests pass (37/37), the pattern matching is precise, and the inline documentation is excellent.

github run

@james-elicx
james-elicx marked this pull request as ready for review May 20, 2026 20:20
@james-elicx
james-elicx merged commit a4ec9af into main May 21, 2026
36 checks passed
@james-elicx
james-elicx deleted the fix/issue-1340-server-actions-errors branch May 21, 2026 08:57
james-elicx added a commit that referenced this pull request Jun 8, 2026
…#1340) (#1853)

* fix(actions): return 404 for MPA action on app with no server actions (#1340)

A multipart form POST to an App Router page is always a server-action
attempt. When the form body decodes to no action at all (e.g. the build
has no server actions, so decodeAction returns null rather than
throwing), vinext fell through to a 200 page render instead of Next.js'
404 + x-nextjs-action-not-found. The fetch-action variant (via the
Next-Action header) already worked; only the MPA/no-JS form-POST variant
was wrong.

Gate the new 404 on the posted-to route being a page: route handlers run
after action dispatch and legitimately accept raw multipart POSTs, so
they still fall through. The page-vs-route distinction uses the static
__loadPage / __loadRouteHandler markers, available before lazy module
hydration.

Finishes the no-server-actions MPA case of #1340 (error-boundary,
content-type, and unrecognized-action handling landed in #1386/#1668).

* test(actions): add MPA no-action integration test; skip redundant route match (#1340)

Address ask-bonk review on #1853:
- Add a dev-server integration test that POSTs multipart/form-data to a
  page (/about) and asserts 404 + x-nextjs-action-not-found, locking in
  the entry-side matchRoute + __loadPage/__loadRouteHandler classification
  end-to-end (previously only unit-tested by passing hasPageRoute directly).
- Gate the progressive-action route match on isProgressiveServerActionRequest
  so matchRoute no longer runs on every App Router request — only for actual
  multipart/no-actionId POST candidates.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant