Hmbown/CodeWhale · warning · FormBodyError

invalid Content-Length

Error message

invalid Content-Length

What it means

FormBodyError with HTTP 400: a Content-Length header was present but did not match /^\d+$/ - it contained a sign, whitespace, hex, or garbage. The check runs before any body read so the size limit decision never trusts a malformed value.

Source

Thrown at web/lib/bounded-form.ts:22

    message: string
  ) {
    super(message);
    this.name = "FormBodyError";
  }
}

export async function readBoundedUrlEncodedForm(
  request: Request,
  maxBytes: number
): Promise<URLSearchParams> {
  const mediaType = request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
  if (mediaType !== "application/x-www-form-urlencoded") {
    throw new FormBodyError(415, "expected application/x-www-form-urlencoded");
  }

  const rawLength = request.headers.get("content-length");
  if (rawLength !== null) {
    if (!/^\d+$/.test(rawLength)) throw new FormBodyError(400, "invalid Content-Length");
    if (Number(rawLength) > maxBytes) throw new FormBodyError(413, "payload too large");
  }

  if (!request.body) return new URLSearchParams();

  const reader = request.body.getReader();
  const chunks: Uint8Array[] = [];
  let total = 0;
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    total += value.byteLength;
    if (total > maxBytes) {
      try {
        await reader.cancel("payload too large");
      } catch {
        // A source cancellation error must not obscure the enforced size limit.
      }

View on GitHub (pinned to 8880682c63)

Solutions

  1. Remove the custom Content-Length and let the HTTP stack compute it
  2. Fix middleware/proxy logic that rewrites the header
  3. Replay the request without the header to confirm it was the cause

Example fix

// before
fetch(url, { method: 'POST', headers: { 'content-length': String(payload.length * 2) }, body: payload });

// after - omit content-length; the client computes it
fetch(url, { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: payload });
Defensive patterns

Strategy: validation

Validate before calling

const len = headers.get('content-length');
if (len !== null && !/^\d+$/.test(len)) {
  headers.delete('content-length'); // let the transport recompute it
}

Try / catch

try {
  const params = await readBoundedUrlEncodedForm(request, MAX_BYTES);
} catch (err) {
  if (err instanceof FormBodyError && err.status === 400) {
    return new Response('malformed request headers', { status: 400 });
  }
  throw err;
}

Prevention

When it happens

Trigger: A client, proxy, or middleware injecting a malformed Content-Length such as ' 123', '-1', '0x40', or '1e3'; test tooling overriding the header by hand.

Common situations: Devtools/Postman manual overrides; a middleware recomputing the header from a float; hand-rolled HTTP clients that format numbers badly.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@8880682c63 (2026-08-16). Data as JSON: /api/errors/d06e38a0440d0d84. Report an issue: GitHub.