{"record":{"id":"c59ad3b187c52113","repo":"nodejs/node","slug":"content-length-mismatch","errorCode":null,"errorMessage":"Content-Length mismatch","messagePattern":"Content-Length mismatch","errorType":"exception","errorClass":"RequestRetryError","httpStatus":null,"severity":"error","filePath":"deps/undici/src/lib/handler/retry-handler.js","lineNumber":30,"sourceCode":"function calculateRetryAfterHeader (retryAfter) {\n  const retryTime = new Date(retryAfter).getTime()\n  return isNaN(retryTime) ? null : retryTime - Date.now()\n}\n\nfunction validatePartialResponseContentLength (headers, range, statusCode, retryCount) {\n  const contentLength = headers['content-length']\n  if (contentLength == null) {\n    return\n  }\n\n  if (!Number.isFinite(range.start) || !Number.isFinite(range.end)) {\n    return\n  }\n\n  const length = Number(contentLength)\n  const expectedLength = range.end - range.start + 1\n  if (!Number.isFinite(length) || length !== expectedLength) {\n    throw new RequestRetryError('Content-Length mismatch', statusCode, {\n      headers,\n      data: { count: retryCount }\n    })\n  }\n}\n\n// A stable controller handed to the downstream handler for the lifetime of the\n// request. Each transparent retry/resume is a *separate* dispatch with its\n// *own* connection controller. Without a stable proxy the downstream body keeps\n// flow-controlling the original (now-dead) controller while data flows on the\n// new one: backpressure pauses the new connection's controller, but the\n// consumer's resume() targets the old one, so the resumed body stalls forever.\n// The proxy always forwards to the controller of the currently active connection.\nclass RetryController {\n  constructor () {\n    this.target = null\n  }\n","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/deps/undici/src/lib/handler/retry-handler.js#L12-L48","documentation":"Thrown by validatePartialResponseContentLength inside the RetryHandler when resuming a partially consumed response. After a mid-stream failure the retry handler re-requests a byte range; if the server's 206 response carries a Content-Length that does not equal range.end - range.start + 1, the resume would produce corrupt data, so the handler refuses it. This protects callers from silently truncated or overlapping bodies.","triggerScenarios":"Using the retry() interceptor (or Agent with retryOptions) against a server that returns a 206 with a Content-Length inconsistent with the requested Range, or a server/proxy that rewrites Content-Length on ranged responses.","commonSituations":"CDNs/proxies that mangle Content-Length on ranged requests; servers without proper Range support that still answer 206; concurrent writes changing the resource mid-download so the range window shifts.","solutions":["Disable range-based resume for that endpoint (avoid the retry interceptor, or set retryOptions that do not resume).","Verify the server returns correct Content-Length for Range requests (test with curl -r).","If the resource is mutable, fetch it atomically without partial retries, or use a strong ETag/If-Match guard at the application layer."],"exampleFix":"// before\nclient = client.compose(interceptors.redirect({ maxRedirections: 5 }), interceptors.retry())\n\n// after (server does not honor Range correctly -> do not retry-resume)\nclient = client.compose(interceptors.redirect({ maxRedirections: 5 }))\n// or fetch the whole body without transparent retry on partial reads","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await client.request({ path, headers: { range: `bytes=${start}-` } }) } catch (e) { if (e.code === 'UND_ERR_REQ_RETRY' && /Content-Length mismatch/.test(e.message)) { start = 0; await client.request({ path }) /* restart */ } else throw e }","preventionTips":["Test origin Range support with curl -r before relying on resume.","Avoid the retry interceptor for origins known to mishandle Content-Length.","Restart from byte 0 on integrity failures."],"tags":["undici","http","retry","range","partial-content","resume"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}