Bug Description
HTTP/2 successful CONNECT / WebSocket upgrade stall queued requests
Reproducible By
import { createSecureServer } from 'node:http2'
import { once } from 'node:events'
import { setTimeout as sleep } from 'node:timers/promises'
import { generate } from '@metcoder95/https-pem'
import { Client } from 'undici'
let postReachedServer = false
const server = createSecureServer({
...(await generate({ opts: { keySize: 2048 } })),
settings: { enableConnectProtocol: true }
})
server.on('stream', (stream, headers) => {
if (headers[':method'] === 'CONNECT' && headers[':protocol'] === 'websocket') {
stream.respond({ ':status': 200 })
return
}
if (headers[':method'] === 'POST') {
postReachedServer = true
stream.resume()
stream.respond({ ':status': 200 })
stream.end('ok')
return
}
stream.close()
})
server.listen(0)
await once(server, 'listening')
const client = new Client(`https://localhost:${server.address().port}`, {
connect: { rejectUnauthorized: false }
})
let upgraded
try {
const upgrade = client.upgrade({
path: '/',
protocol: 'websocket'
})
const post = client.request({
method: 'POST',
path: '/',
body: 'hello'
})
upgraded = await upgrade
console.log('upgrade resolved')
const result = await Promise.race([
post.then(({ statusCode }) => ({ statusCode })),
sleep(1000).then(() => null)
])
if (result == null) {
throw new Error(`POST stalled; reached server: ${postReachedServer}`)
}
console.log(`POST resolved with status ${result.statusCode}`)
} finally {
upgraded?.socket.on('error', () => {})
upgraded?.socket.end()
await client.destroy()
await new Promise(resolve => server.close(resolve))
}
Expected Behavior
After the upgrade request successfully completes from Undici’s dispatcher perspective, the client should resume dispatching queued work.
For the minimal repro, the output should be
upgrade resolved
POST resolved with status 200
Logs & Screenshots
The WebSocket upgrade succeeds, but the queued POST never reaches the server.
For the minimal repro, the output is
upgrade resolved
file:///Users/trivikram/workspace/test-repro/test.mjs:62
throw new Error(`POST stalled; reached server: ${postReachedServer}`)
^
Error: POST stalled; reached server: false
at file:///Users/trivikram/workspace/test-repro/test.mjs:62:11
Environment
macOS 26.4.1
Node v24.15.0
undici v8.1.0
Bug Description
HTTP/2 successful CONNECT / WebSocket upgrade stall queued requests
Reproducible By
Expected Behavior
After the upgrade request successfully completes from Undici’s dispatcher perspective, the client should resume dispatching queued work.
For the minimal repro, the output should be
Logs & Screenshots
The WebSocket upgrade succeeds, but the queued POST never reaches the server.
For the minimal repro, the output is
Environment
macOS 26.4.1
Node v24.15.0
undici v8.1.0