Hmbown/CodeWhale · error · BodyReadError

invalid Content-Length

Error message

invalid Content-Length

What it means

BodyReadError with status 400 raised by readBoundedBody's reject() helper when the request's Content-Length header is missing or not a valid integer. Content-Length is used only as an early rejection signal; a malformed value means the HTTP request itself is malformed, so reading is aborted and the stream cancelled before any bytes are consumed.

Solutions

  1. Fix the client to send a correct decimal byte count for Content-Length, or omit it and use chunked transfer encoding.
  2. Remove stray characters/units from the manually set Content-Length header.
  3. If behind a proxy, check whether the proxy mangles the header and correct its configuration.

Example fix

// before
curl -H 'Content-Length: 12 bytes' -d 'token=x' https://host/api/admin/login
// after
curl -d 'token=x' https://host/api/admin/login
Defensive patterns

Strategy: validation

Validate before calling

const cl = request.headers.get("content-length");
if (cl !== null && !/^\d+$/.test(cl)) throw new Error("client sent invalid Content-Length");

Try / catch

try {
  const body = await readBoundedBody(request, MAX);
} catch (err) {
  if (err instanceof BodyReadError && err.status === 400 && err.message === "invalid Content-Length") {
    return new Response("invalid Content-Length header", { status: 400 });
  }
  throw err;
}

Prevention

When it happens

Trigger: A client sends a Content-Length header that is not all digits — e.g. `"abc"`, negative values, or a header containing whitespace/units — to any route using readBoundedBody (such as the admin form POST).

Common situations: Hand-rolled HTTP clients or curl scripts with a malformed -H 'Content-Length: ...'; misbehaving proxies rewriting the header; fuzzed or hostile traffic (the check is intentional hardening).

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15). Data as JSON: /api/errors/1c74d3c72aa9e99e. Report an issue: GitHub.

Appendix: source

Thrown at web/lib/bounded-body.ts:12

export class BodyReadError extends Error {
  constructor(readonly status: 400 | 413, message: string) {
    super(message);
    this.name = "BodyReadError";
  }
}

/** Count raw bytes while reading; Content-Length is only an early rejection. */
export async function readBoundedBody(request: { body: ReadableStream<Uint8Array> | null; headers: Headers }, maxBytes: number): Promise<Uint8Array> {
  const reject = async (status: 400 | 413, message: string): Promise<never> => {
    try { await request.body?.cancel(message); } catch { /* Keep the original rejection. */ }
    throw new BodyReadError(status, message);
  };
  const rawLength = request.headers.get("content-length");
  if (rawLength !== null) {
    if (!/^\d+$/.test(rawLength)) return reject(400, "invalid Content-Length");
    if (Number(rawLength) > maxBytes) return reject(413, "payload too large");
  }
  if (!request.body) return new Uint8Array();

  const reader = request.body.getReader();
  const chunks: Uint8Array[] = [];
  let total = 0;
  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      total += value.byteLength;
      if (total > maxBytes) throw new BodyReadError(413, "payload too large");
      chunks.push(value);

View on GitHub (pinned to 433685b202)