Skip to content

[vite-plugin] WebSocket upgrade forwards Accept-Encoding gzip; Worker throw becomes undici JSON.parse crash #15199

Description

@quantizor

What versions & operating system are you using?

  • macOS (darwin 25.6.0), arm64
  • Node v24.11.0
  • @cloudflare/vite-plugin 1.43.0
  • wrangler 4.107.0
  • miniflare 4.20260701.0
  • undici 7.28.0

Please provide a link to a minimal reproduction

cloudflare/vinext#2921

Describe the Bug

handleWebSocket (packages/vite-plugin-cloudflare/src/websockets.ts) forwards a browser WebSocket upgrade to miniflare.dispatchFetch with createHeaders(request), which copies every request header, including Accept-Encoding: gzip.

When the Worker throws on that upgrade, workerd returns a 500 JSON error with Content-Encoding: gzip and MF-Experimental-Error-Stack. miniflare's dispatchFetch then calls response.json() without decompressing (DispatchFetchDispatcher does not inflate). That rejects with:

SyntaxError: Unexpected token '\u001f', "\u001f�\b..." is not valid JSON

On 1.43.0 the upgrade listener is async with no try/catch, so the rejection is unhandled and Node exits (exit 7). Vite HMR sockets (sec-websocket-protocol starting with vite) are skipped; Worker sockets are not.

On current main, #14862 wraps dispatchFetch in try/catch and socket.destroy()s. That stops the process death (good) but:

  1. The Worker's exception is never revived or logged. The gzip SyntaxError is swallowed.
  2. The client still gets no HTTP response. That is [vite-plugin] Non-101 WebSocket upgrade responses are destroyed instead of delivered to the client in dev #15170 for the non-throwing non-101 case; a throwing Worker never even reaches the !response.webSocket branch, because dispatchFetch throws first.

Two complementary fixes, neither of which is "absorb gzip in the app":

  1. Do not forward Accept-Encoding (and other content-coding headers) on the synthetic upgrade dispatchFetch. A 101 has no body; a 500 error page should not be gzipped into response.json().
  2. Keep the [vite-plugin] Fix flaky WebSocket upgrade test and harden upgrade handler #14862 catch, but log / reviveError the rejection, and write a non-101 response when there is one ([vite-plugin] Non-101 WebSocket upgrade responses are destroyed instead of delivered to the client in dev #15170).

The parse itself belongs in miniflare (sibling issue). vinext currently absorbs the gzip SyntaxError so vinext dev does not exit: cloudflare/vinext#2922

Please provide any relevant error logs

SyntaxError: Unexpected token '\u001f', "\u001f�\b..." is not valid JSON
    at JSON.parse (<anonymous>)
    at parseJSONFromBytes (undici)
    at Miniflare.dispatchFetch (miniflare)
    at Server.<anonymous> (@cloudflare/vite-plugin handleWebSocket upgrade)

Node then exits with code 7 on 1.43.0 because vinext's socket-error backstop rethrows anything that is not a peer-disconnect / benign asset import.

Metadata

Metadata

Assignees

No one assigned

    Labels

    package:vite-pluginRelating to the `@cloudflare/vite-plugin` package

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions