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

  1. Retry the request — a length bug on one response often does not recur; h2 will usually open a new stream/connection.
  2. Test the URL with curl --http2 to confirm the origin itself is inconsistent (`curl -v --http2 url 2>&1 | grep -i content-length`).
  3. If you control the server/proxy: ensure body transforms run before content-length is set, or use chunked/stream framing instead of fixed lengths.
  4. 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

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


AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16). Data as JSON: /api/errors/79896cef712c9781. Report an issue: GitHub.