Follow the Fetch spec for statusText under pedantic_wpt by npaun · Pull Request #7839 · cloudflare/workerd · GitHub
Skip to content

Follow the Fetch spec for statusText under pedantic_wpt - #7839

Draft
npaun wants to merge 1 commit into
mainfrom
npaun/pedantic-wpt-status-text
Draft

npaun wants to merge 1 commit into
mainfrom
npaun/pedantic-wpt-status-text

Conversation

@npaun

@npaun npaun commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Summary

The Fetch spec gives a response an empty status message unless something sets it. ResponseInit.statusText defaults to "", and only the scheme fetch steps set a value: OK for data:/about:blank, and OK/Partial Content for blob:. workerd instead derives the reason phrase from the status code, so new Response(null, { status: 404 }).statusText is "Not Found", and the same goes for Response.json() and Response.redirect().

With pedantic_wpt enabled:

  • An unset statusText is "".
  • A Response initialized from another Response (constructor or Response.json()) copies its status message exactly.
  • fetch() responses keep the reason phrase the server sent, even when it matches the default.

data: URL fetches now set "OK" explicitly, which is the value they already reported. workerd doesn't fetch blob: or about: URLs.

The HTTP reason phrase on the wire is unchanged. An unset status message still sends the conventional phrase (new getReasonPhrase()), so HTTP clients see no difference. Error messages that include the status, such as Fetcher's put()/get() helpers, use the same phrase.

Fixes #4973

Compatibility

Every visible change is gated on the existing pedantic_wpt flag. Without it, statusText behaves as before. WPT always runs with pedantic_wpt (build/wpt_test.bzl), so the statusText expected failures in response-init-001, response-static-redirect and response-static-json are removed from both fetch WPT configs.

Open issue: statusText across JS RPC when the two Workers' flags differ

When a Response crosses JS RPC, Response::serialize sends the stored status message, and omits it entirely when it's unset. deserialize rebuilds the Response without one, so the receiving Worker's flag decides what "unset" means. But "unset" means different things on each side: without pedantic_wpt it means "use the default phrase", and with it it means "".

If the two Workers' pedantic_wpt settings differ, they see different values for the same Response:

Sender Response Sender sees Receiver sees
pedantic_wpt new Response(null, { status: 404 }) "" "Not Found" (receiver without the flag)
no flag new Response(null, { status: 404 }) "Not Found" "" (receiver with pedantic_wpt)
no flag fetch() result where the origin sent 404 Not Found "Not Found" "" (receiver with pedantic_wpt)

The last row is the worst. Without the flag, makeHttpResponse doesn't store a reason phrase that matches the default. So a pedantic_wpt Worker that receives a proxied fetch result over RPC reports "", even though the origin really sent Not Found. That information is lost.

Mixed settings aren't hypothetical: RPC between Workers owned by different teams is common. It also matters more if pedantic_wpt ever gets an enable date, because then mixed settings become the normal state during the transition.

Proposed fix: make the serialized form say exactly what the sender sees. serialize would write getStatusText(js), the sender's effective value, explicitly, so the receiver always sees what the sender saw. Pieces:

  • A pedantic_wpt sender's unset value then arrives as an explicit "". If a receiver without the flag returns that Response to a client, getReasonPhrase() would send an empty reason phrase, because it only falls back when the value is unset. getReasonPhrase() should therefore also fall back on "". That changes the wire for an explicitly empty statusText (today it sends an empty phrase), but HTTP clients don't depend on the reason phrase.
  • An older runtime receiving this just sees an explicit statusText, so there's no version-skew problem.

This isn't implemented in this PR yet. Feedback welcome on whether to fold it in here, before this lands.

The Fetch spec gives a response an empty status message unless
something sets it: ResponseInit.statusText defaults to "", and
"initialize a response" copies it as-is. Only the scheme fetch
steps set one, to `OK` for data: and about:blank URLs and to `OK`
or `Partial Content` for blob: URLs. workerd instead derives the
RFC 9110 reason phrase from the status code, so
`new Response(null, { status: 404 }).statusText` is "Not Found"
rather than "", and likewise for Response.json() and
Response.redirect().

With pedantic_wpt enabled, an unset statusText is now "". A
Response initialized from another Response, through the
constructor or Response.json(), copies its status message
exactly. Responses returned by fetch() keep the reason phrase the
server sent, even when it matches the status code's default.
data: URL fetches now set "OK" explicitly, which is the value they
already reported. workerd does not fetch blob: or about: URLs.

The HTTP reason phrase sent for a Response returned to a client is
unchanged: an unset status message still sends the status code's
conventional phrase, so HTTP clients see no difference even though
the JS-visible statusText is empty. Error messages that include a
response's status, such as those thrown by Fetcher's put() and
get() helpers, use the same conventional phrase.

Compatibility: the observable change is gated behind the existing
pedantic_wpt flag, which has no enable date. Without it, statusText
is unchanged.

Testing: tests were not run locally because a WARP outage made
the cfdata.org dependency, remote cache and credential hosts that
edgeworker's bazel needs unreachable. CI is the verification.

Fixes #4973

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@npaun
npaun force-pushed the npaun/pedantic-wpt-status-text branch from a29352e to c10c3c3 Compare October 20, 2026 04:47
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.

If pedantic_wpt is set, make sure statusText is only set when the fetch spec says so

1 participant