nodejs/node · error · RequestRetryError
Content-Range mismatch
Error message
Content-Range mismatch
What it means
Thrown by RetryHandler.onResponseStart during a retry resume when the resumed response is expected to carry a Content-Range header (it is a 206 or a 200 resuming from start>0) but parseRangeHeader returns null, i.e. the header is missing or unparseable. Without a Content-Range the handler cannot verify it is resuming from the correct byte offset.
Source
Thrown at deps/undici/src/lib/handler/retry-handler.js:283
// Checkpoint for resume from where we left it
if (this.headersSent) {
// Only Partial Content 206 supposed to provide Content-Range,
// any other status code that partially consumed the payload
// should not be retried because it would result in downstream
// wrongly concatenate multiple responses.
if (statusCode !== 206 && (this.start > 0 || statusCode !== 200)) {
throw new RequestRetryError('server does not support the range header and the payload was partially consumed', statusCode, {
headers,
data: { count: this.retryCount }
})
}
const contentRange = parseRangeHeader(headers['content-range'])
// If no content range
if (!contentRange) {
// We always throw here as we want to indicate that we entred unexpected path
throw new RequestRetryError('Content-Range mismatch', statusCode, {
headers,
data: { count: this.retryCount }
})
}
// Let's start with a weak etag check
if (this.etag != null && this.etag !== headers.etag) {
// We always throw here as we want to indicate that we entred unexpected path
throw new RequestRetryError('ETag mismatch', statusCode, {
headers,
data: { count: this.retryCount }
})
}
validatePartialResponseContentLength(headers, contentRange, statusCode, this.retryCount)
const { start, size, end = size ? size - 1 : null } = contentRange
View on GitHub (pinned to 1b2de5e052)
Solutions
- Confirm the server returns a well-formed Content-Range header (e.g. 'bytes 1000-1999/5000') on ranged responses.
- Disable transparent retry-resume for origins that do not send Content-Range.
- Validate the header with curl -I -H 'Range: bytes=0-0' before relying on resume.
Example fix
// before (relying on retry resume against an origin that strips Content-Range) client = client.compose(interceptors.retry()) // after // verify with: curl -r 0-0 -I https://example.com/file (look for content-range) // if missing, drop the retry interceptor and fetch atomically: client = baseClient
Defensive patterns
Strategy: retry
Try / catch
try { await retryClient.request(opts) } catch (e) { if (e.code === 'UND_ERR_REQ_RETRY' && /Content-Range mismatch/.test(e.message)) { await baseClient.request(opts) } else throw e } Prevention
- Verify Content-Range presence on ranged responses (curl -I -H 'Range: bytes=0-0').
- Drop the retry interceptor for origins that strip Content-Range.
- Restart atomically when the header is missing.
When it happens
Trigger: A resume request gets a 2xx response that lacks the Content-Range header, or carries a malformed Content-Range that parseRangeHeader cannot interpret.
Common situations: Servers that answer 200 to Range requests without Content-Range; intermediary proxies that strip Content-Range; misconfigured compression that removes the header; buggy origin returning partial content without metadata.
Related errors
- Content-Length mismatch
- server does not support the range header and the payload was
- ETag mismatch
- Request failed
- UND_ERR_INVALID_ARG
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/aee88da6aada48bb.
Report an issue: GitHub.