oven-sh/bun · error · TypeError
HTTP2ContentLengthMismatch
HTTP2ContentLengthMismatch
Error message
HTTP2ContentLengthMismatch
What it means
On stream completion, the raw count of HTTP/2 DATA bytes received does not equal the Content-Length announced in HEADERS (src/http/h2_client/ClientSession.rs:1243-1248; RFC 9113 §8.1.1 declares a mismatch malformed). The body handler clamps its counter at content_length, so this catches undershoot and overshoot (truncated or over-long bodies) and fails the request instead of delivering corrupt data.
Source
Thrown at src/http/error.rs:12
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
pub enum Error {
#[error("CompressionFailed")]
CompressionFailed,
#[error("Aborted")]
Aborted,
#[error("WriteFailed")]
WriteFailed,
#[error("HTTP2RefusedStream")]
HTTP2RefusedStream,
#[error("HTTP2ContentLengthMismatch")]
HTTP2ContentLengthMismatch,
#[error("HTTP2FrameSizeError")]
HTTP2FrameSizeError,
#[error("HTTP2ProtocolError")]
HTTP2ProtocolError,
#[error("HTTP2FlowControlError")]
HTTP2FlowControlError,
#[error("HTTP2EnhanceYourCalm")]
HTTP2EnhanceYourCalm,
#[error("HTTP2HeaderListTooLarge")]
HTTP2HeaderListTooLarge,
#[error("HTTP2StreamReset")]
HTTP2StreamReset,
#[error("HTTP2GoAway")]
HTTP2GoAway,
#[error("HTTP2CompressionError")]
HTTP2CompressionError,
#[error("Timeout")]View on GitHub (pinned to 8c5296ac45)
Solutions
- Retry the request — a length bug on one response often does not recur; h2 will usually open a new stream/connection.
- Test the URL with curl --http2 to confirm the origin itself is inconsistent (`curl -v --http2 url 2>&1 | grep -i content-length`).
- If you control the server/proxy: ensure body transforms run before content-length is set, or use chunked/stream framing instead of fixed lengths.
- Report to the endpoint owner if curl reproduces the mismatch — this is a server-side protocol violation.
Example fix
// before
const data = await (await fetch(url)).json(); // HTTP2ContentLengthMismatch kills the parse
// after: bounded retry for the malformed-response case
async function getJson(url, tries = 3) {
for (let i = 0; ; i++) {
try { return await (await fetch(url)).json(); }
catch (e) {
if (i >= tries - 1 || !String(e.message).includes('HTTP2ContentLengthMismatch')) throw e;
await Bun.sleep(100 * (i + 1));
}
}
} Defensive patterns
Strategy: retry
Type guard
function isContentLengthMismatch(err) {
return /HTTP2ContentLengthMismatch/i.test(String(err?.message ?? err));
} Try / catch
async function fetchRetryMalformed(url, init, tries = 3) {
for (let i = 0; ;i++) {
try { return await fetch(url, init); }
catch (err) {
if (i >= tries - 1 || !isContentLengthMismatch(err)) throw err;
await Bun.sleep(100 * (i + 1));
}
}
} Prevention
- Reproduce with `curl -v --http2 <url>` before assuming a client bug — this is a server-side protocol violation
- If you operate the origin/proxy: never rewrite bodies after content-length is computed
- Prefer streamed/chunked responses on origins that transform bodies
When it happens
Trigger: An h2 server (or intermediate proxy translating h1<->h2) sends a Content-Length that disagrees with the actual DATA frames — e.g. wrong content-length after body rewrites, gzip applied after length computation, or a proxy mangling chunked->content-length conversion. Also triggered by truncation where the peer half-closes early.
Common situations: MITM/corporate proxies that modify bodies (compression, script injection) without fixing content-length; buggy h2 origins behind envoy/nginx; responses corrupted by middleboxes; rare server bugs after a firmware update.
Related errors
- HTTP2RefusedStream
- HEAD ${url}: ${head.status}
- Range ${url}: ${res.status}
- FailedToOpenSocket
- CompressionFailed
AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16).
Data as JSON: /api/errors/79896cef712c9781.
Report an issue: GitHub.