Skip to content

[vite-plugin] Preserve Worker response statusText in dev and preview - #16453

Open
james-elicx wants to merge 1 commit into
mainfrom
vite-plugin/preserve-status-text
Open

james-elicx wants to merge 1 commit into
mainfrom
vite-plugin/preserve-status-text

Conversation

@james-elicx

Copy link
Copy Markdown
Member

A Worker response such as new Response(body, { status: 231, statusText: "global-before" }) reached the browser through the Vite plugin as 231 unknown.

The plugin writes Worker responses to Node with sendResponse from @remix-run/node-fetch-server. That calls res.writeHead(response.status, headers) without a reason phrase, so Node uses its default text for the status code, or unknown for non-standard codes. Miniflare itself keeps the status text (dispatchFetch returns 231 "global-before"), so the text is lost only at this step.

createRequestHandler now sets res.statusMessage = response.statusText before calling sendResponse when the response has a status text. HTTP/2 is skipped because it has no reason phrase, and setting statusMessage on an Http2ServerResponse only produces a warning. All the plugin's dev, preview and trigger-handler middleware go through createRequestHandler, so they all get the fix.

This could also be fixed upstream in @remix-run/node-fetch-server by passing response.statusText to writeHead. The fix here doesn't depend on that, and still works if upstream changes.


  • Tests
    • Tests included/updated
    • Automated tests not possible - manual testing has been completed as follows:
    • Additional testing not necessary because:
  • Public documentation
    • Cloudflare docs PR(s):
    • Documentation not necessary because: this is a bug fix that makes dev and preview match production behaviour

sendResponse from @remix-run/node-fetch-server calls res.writeHead(status,
headers) without a reason phrase, so Node replaced the Worker's statusText
with its default (or "unknown" for non-standard codes). Set
res.statusMessage before writing the response on HTTP/1.x.
@changeset-bot

changeset-bot Bot commented Oct 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: b7382af

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

This PR includes changesets to release 1 package
Name Type
@cloudflare/vite-plugin 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

@ask-bonk

ask-bonk Bot commented Oct 23, 2026

Copy link
Copy Markdown
Contributor

LGTM!

github run

@james-elicx
james-elicx marked this pull request as ready for review October 23, 2026 22:49
@workers-devprod
workers-devprod requested review from a team and petebacondarwin and removed request for a team October 23, 2026 22:49
@workers-devprod

Copy link
Copy Markdown
Contributor

Codeowners approval required for this PR:

  • @cloudflare/wrangler
Show detailed file reviewers
  • .changeset/vite-plugin-preserve-status-text.md: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/src/tests/utils.spec.ts: [@cloudflare/wrangler]
  • packages/vite-plugin-cloudflare/src/utils.ts: [@cloudflare/wrangler]

@devin-ai-integration devin-ai-integration 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.

Devin Review found 1 potential issue.

Devin Review

response = new MiniflareResponse(response.body, response);
// HTTP/2 disallows use of the `transfer-encoding` header
response.headers.delete("transfer-encoding");
} else if (response.statusText) {

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.

🔍 Empty status text still gains a default

For a Worker response with statusText: "", the guard skips res.statusMessage. Node then sends its default phrase; status 231 arrives as 231 unknown.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@clanky-the-clanker

Copy link
Copy Markdown

✅ Approved — A small, well-scoped fix: res.statusMessage is set from the Worker's statusText for HTTP/1.x only, before sendResponse writes the response. Tests cover both a custom status text and the default text, and a changeset is included. No issues found.

The decision is advisory: this comment never approves, blocks or requests changes on the pull request.


✅ All checks passed

📋 Code Quality: ✅ LGTM

I found no problems in this change.

  • HTTP/1.x only: the new else if (response.statusText) branch in createRequestHandler (packages/vite-plugin-cloudflare/src/utils.ts) runs only when the request is not HTTP/2. That avoids the warning Node gives for setting statusMessage on an Http2ServerResponse.
  • Default text still works: when no statusText is set, the branch is skipped and Node falls back to its default reason phrase.
  • No header-injection risk: the Response constructor already rejects a statusText that isn't a valid reason phrase, so a CR or LF can't reach writeHead.
  • Tests: the new parameterised test starts a real HTTP server and checks both cases (custom 231 global-before and default 404 Not Found). It takes expect from the test context, as the repository's testing conventions ask.

📝 Documentation: ✅ LGTM

The changeset describes the bug fix clearly, and the code comment explains why statusMessage has to be set before sendResponse is called. No public documentation needs to change, because this makes dev and preview behave like production.


Reviewed commit: b7382af4

@pkg-pr-new

pkg-pr-new Bot commented Oct 23, 2026

Copy link
Copy Markdown
@cloudflare/autoconfig

npm i https://pkg.pr.new/@cloudflare/autoconfig@16453

@cloudflare/build-output-utils

npm i https://pkg.pr.new/@cloudflare/build-output-utils@16453

@cloudflare/codemods

npm i https://pkg.pr.new/@cloudflare/codemods@16453

@cloudflare/config

npm i https://pkg.pr.new/@cloudflare/config@16453

@cloudflare/containers-shared

npm i https://pkg.pr.new/@cloudflare/containers-shared@16453

create-cloudflare

npm i https://pkg.pr.new/create-cloudflare@16453

@cloudflare/deploy-helpers

npm i https://pkg.pr.new/@cloudflare/deploy-helpers@16453

@cloudflare/kv-asset-handler

npm i https://pkg.pr.new/@cloudflare/kv-asset-handler@16453

miniflare

npm i https://pkg.pr.new/miniflare@16453

@cloudflare/pages-functions

npm i https://pkg.pr.new/@cloudflare/pages-functions@16453

@cloudflare/pages-shared

npm i https://pkg.pr.new/@cloudflare/pages-shared@16453

@cloudflare/runtime-types

npm i https://pkg.pr.new/@cloudflare/runtime-types@16453

@cloudflare/unenv-preset

npm i https://pkg.pr.new/@cloudflare/unenv-preset@16453

@cloudflare/vite-plugin

npm i https://pkg.pr.new/@cloudflare/vite-plugin@16453

@cloudflare/vitest-plugin

npm i https://pkg.pr.new/@cloudflare/vitest-plugin@16453

@cloudflare/workers-auth

npm i https://pkg.pr.new/@cloudflare/workers-auth@16453

@cloudflare/workers-editor-shared

npm i https://pkg.pr.new/@cloudflare/workers-editor-shared@16453

@cloudflare/workers-utils

npm i https://pkg.pr.new/@cloudflare/workers-utils@16453

wrangler

npm i https://pkg.pr.new/wrangler@16453

commit: b7382af

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

Labels

None yet

Projects

Status: Untriaged

Development

Successfully merging this pull request may close these issues.

2 participants