Bug Description
Undici’s Content-Range parser rejects valid RFC 9110 byte range headers where the complete representation length is unknown, such as:
Content-Range: bytes 0-499/*
Reproducible By
import { equal } from 'node:assert/strict'
import { once } from 'node:events'
import { createServer } from 'node:http'
import { Client, RetryHandler } from 'undici'
let requestCount = 0
const chunks = []
const server = createServer({ joinDuplicateHeaders: true }, (req, res) => {
requestCount++
if (requestCount === 1) {
equal(req.headers.range, 'bytes=0-3')
res.setHeader('etag', '"same"')
res.write('abc')
setTimeout(() => res.destroy(), 50)
return
}
equal(req.headers.range, 'bytes=3-')
res.writeHead(206, {
etag: '"same"',
'content-range': 'bytes 3-5/*'
})
res.end('def')
})
server.listen(0)
await once(server, 'listening')
const client = new Client(`http://localhost:${server.address().port}`)
try {
const { promise, resolve, reject } = Promise.withResolvers();
const handler = new RetryHandler({
method: 'GET',
path: '/',
headers: {},
retryOptions: {
minTimeout: 1,
retry: (err, _ctx, done) => {
if (err.message === 'Content-Range mismatch') {
done(err)
return
}
done(null)
}
}
}, {
dispatch: client.dispatch.bind(client),
handler: {
onResponseStart () {
return true
},
onResponseData (_controller, chunk) {
chunks.push(chunk)
return true
},
onResponseEnd () {
resolve()
},
onResponseError (_controller, err) {
reject(err)
}
}
})
client.dispatch({
method: 'GET',
path: '/',
headers: {
range: 'bytes=0-3'
}
}, handler)
await promise
equal(Buffer.concat(chunks).toString(), 'abcdef')
} finally {
await client.close()
server.close()
await once(server, 'close')
}
Expected Behavior
No error since the Content-Range is valid
Logs & Screenshots
It throws error
RequestRetryError: Content-Range mismatch
at RetryHandler.onResponseStart (/Users/trivikram/workspace/test-repro/node_modules/undici/lib/handler/retry-handler.js:216:15)
at Request.onResponseStart (/Users/trivikram/workspace/test-repro/node_modules/undici/lib/core/request.js:330:39)
at Parser.onHeadersComplete (/Users/trivikram/workspace/test-repro/node_modules/undici/lib/dispatcher/client-h1.js:609:27)
at wasm_on_headers_complete (/Users/trivikram/workspace/test-repro/node_modules/undici/lib/dispatcher/client-h1.js:153:30)
at wasm://wasm/00034eea:wasm-function[10]:0x571
at wasm://wasm/00034eea:wasm-function[20]:0x845f
at Parser.execute (/Users/trivikram/workspace/test-repro/node_modules/undici/lib/dispatcher/client-h1.js:337:22)
at Parser.readMore (/Users/trivikram/workspace/test-repro/node_modules/undici/lib/dispatcher/client-h1.js:301:12)
at Socket.onHttpSocketReadable (/Users/trivikram/workspace/test-repro/node_modules/undici/lib/dispatcher/client-h1.js:885:18)
at Socket.emit (node:events:509:28) {
code: 'UND_ERR_REQ_RETRY',
statusCode: 206,
data: { count: 2 },
headers: {
etag: '"same"',
'content-range': 'bytes 3-5/*',
date: 'Sun, 26 Apr 2026 02:42:45 GMT',
connection: 'keep-alive',
'keep-alive': 'timeout=5',
'transfer-encoding': 'chunked'
}
}
Environment
macOS 26.4.1
Node v24.15.0
undici v8.1.0
Bug Description
Undici’s
Content-Rangeparser rejects valid RFC 9110 byte range headers where the complete representation length is unknown, such as:Reproducible By
Expected Behavior
No error since the Content-Range is valid
Logs & Screenshots
It throws error
Environment
macOS 26.4.1
Node v24.15.0
undici v8.1.0