Repository navigation
Description
Is there an existing issue for this?
- I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- I have reviewed the documentation https://docs.sentry.io/
- I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/nuxt
SDK Version
11.10.0 (does not occur with 11.9.0)
Framework Version
nuxt 4.6.3, nitropack 2.13.5 (preset: cloudflare-module, experimental.websocket: true), h3 1.15.11, crossws 0.3.5
Link to Sentry event
No response
Reproduction Example/SDK Setup
A WebSocket route is defined using defineWebSocketHandler under Nitro's server/routes directory. The ticket is validated within the upgrade hook, and either a Durable Object response or an HTTP 401 Response is returned.
// server/routes/ws/[code].ts
export default defineWebSocketHandler({
upgrade(request) {
const ticket = new URL(request.url).searchParams.get('ticket');
if (ticket !== 'valid') return new Response('Unauthorized', { status: 401 });
},
open(peer) {
peer.send('welcome');
},
});Steps to Reproduce
- Update @sentry/nuxt from 11.9.0 to 11.10.0 with the following configuration, build, and deploy to Cloudflare Workers.
- Connect to
wss://<host>/ws/<code>from a browser. - Connect with an invalid ticket (
?ticket=invalid).
Expected Result
- The
upgradehook is called. - For invalid tickets, the connection is rejected with HTTP 401.
Actual Result
- The
upgradehook is not called. - WebSocket connections are established even with invalid tickets (HTTP 101).
- No messages are sent from the server after connection.
Additional Context
packages/nuxt/src/runtime/plugins/tunnel-legacy.server.ts (#25418), which was added in 11.10.0, directly inserts a layer with an empty route ('') at the beginning of the h3 application's layer list.
nitroApp.h3App.stack.unshift({
route: '',
handler: eventHandler(event => { ... }),
});When passing through h3's use, normalizeLayer normalizes the route with withoutTrailingSlash, so layers with a route of '' are registered as '/'. If inserted directly via stack.unshift, this normalization does not take place, and the route remains ''.
Nitro 2's WebSocket handler resolution logic (h3App.websocket.resolve) inspects layers from the beginning and uses handler.__websocket__ of the first matching layer as the hook. In the deployed bundle, the relevant section was the following code.
for (const layer of stack) {
if (layer.route === "/" && !layer.handler.__resolve__) continue;
if (!path.startsWith(layer.route)) continue;
...
return { route: layer.route, handler: layer.handler };
}Layers with a route of '/' are skipped, but layers with a route of '' are not skipped. Since startsWith('') evaluates to true for all paths, the tunnel layer matches all paths. Because this layer does not have a __websocket__, the hooks become {}.
The Cloudflare adapter in crossws 0.3.5 generates its own WebSocketPair and accepts the connection when the result of the upgrade hook is undefined. As a result, the WebSocket handler defined by the app is not called, and the connection appears successful to the client.
If you set the route to '/' when inserting a layer, that layer is skipped during WebSocket handler resolution. For HTTP request processing, layers with a route length of 1 or less match all paths, so we believe there is no impact on tunnel processing (unverified).
Priority
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it.
Metadata
Metadata
Assignees
Projects
- StatusShow more project fieldsNo status
Activity
linear-code commentedon Nov 2, 2026
JS-4055
chan-mai commentedon Nov 2, 2026
I would like to create a fix PR for this issue. Please let me know if I can start working on it.
This changes the route used when inserting the layer for tunnel processing in
packages/nuxt/src/runtime/plugins/tunnel-legacy.server.tsfrom''to'/'.Nitro 2's WebSocket handler resolution skips layers where the route is
'/'and they do not have__resolve__. Therefore, with this change, the tunnel layer will no longer match during WebSocket handler resolution. For HTTP request processing, layers with a route length of 1 or less match all paths, so we believe the tunnel processing will remain unchanged.The PR is planned to include the following two items:
upgradehook for WebSocket routes is called in Nitro 2Please let me know if you plan to fix this with a different approach.
logaretm commentedon Nov 3, 2026
This will be addressed by #26001, but since that's part of a larger stack, feel free to open a PR with the quick fix ('' -> '/') in the meantime.
chan-mai commentedon Nov 4, 2026
Thank you. I have created a PR for the quick fix: #26082
thoda-dev commentedon Nov 5, 2026
Ran into this this morning after updating the module. I ended up with the workaround below, feel free to use it until the fix is released. It doesn't require pinning
@sentry/nuxt: a Nitro plugin rewrites the route of Sentry's tunnel layer. Plugins inserver/pluginsrun after module plugins, so the layer is already in the stack.''and'/'behave the same for HTTP requests, so the tunnel keeps working, and WebSocket handlers resolve again. It can be removed once #26082 / #26001 is released.andreiborza commentedon Nov 6, 2026
Thank you @chan-mai for the PR and @thoda-dev for the workaround. We'll look at the PR.