can1357/oh-my-pi · error · HTTPException

invalid content-length

Error message

invalid content-length

What it means

_read_body_capped reads request bodies with a size cap. If a Content-Length header is present but not a valid integer (int(cl) raises ValueError), the proxy returns HTTP 400 'invalid content-length'. This protects downstream size enforcement from silently ignoring a malformed header.

Source

Thrown at python/robomp/src/proxy/server.py:423

        return f"{request.url.path}?{query}" if query else request.url.path

    async def _read_body_capped(request: Request) -> bytes:
        """Read the request body with a hard byte cap.

        Checks `Content-Length` first (cheap reject before any read), then
        streams chunks via `request.stream()` with a running counter so a
        client that lies about (or omits) the header still can't get more
        than `max_bytes` into memory. We deliberately do NOT call
        `request.body()` first — that would buffer the full payload before
        auth checks ever run.
        """
        max_bytes = settings.gh_proxy_max_body_bytes
        cl = request.headers.get("content-length")
        if cl is not None:
            try:
                declared = int(cl)
            except ValueError as exc:
                raise HTTPException(400, "invalid content-length") from exc
            if declared > max_bytes:
                raise HTTPException(status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, "request body too large")
        chunks: list[bytes] = []
        total = 0
        async for chunk in request.stream():
            if not chunk:
                continue
            total += len(chunk)
            if total > max_bytes:
                raise HTTPException(status.HTTP_413_REQUEST_ENTITY_TOO_LARGE, "request body too large")
            chunks.append(chunk)
        body = b"".join(chunks)
        # Starlette's `request.body()` / `request.json()` re-read from
        # `request._body`. We consumed the stream above, so seed the cache
        # to keep downstream JSON parsing working without a second read.
        request._body = body  # type: ignore[attr-defined]
        return body

View on GitHub (pinned to 9690622007)

Solutions

  1. Fix the client to send a valid decimal Content-Length (or omit it and use chunked transfer-encoding)
  2. Inspect intermediate proxies/CDNs that may rewrite the header and fix their configuration
  3. Test with curl or a standard HTTP library, which always emits a correct Content-Length for fixed bodies

Example fix

// before
headers: { "Content-Length": String(body.length) + " bytes" }
// after
headers: { "Content-Length": String(body.length) }
Defensive patterns

Strategy: validation

Validate before calling

const cl = headers["content-length"];
if (cl !== undefined && !/^\d+$/.test(cl)) throw new Error(`invalid content-length: ${cl}`);

Type guard

function isValidContentLength(v: unknown): v is string {
  return typeof v === "string" && /^\d+$/.test(v);
}

Prevention

When it happens

Trigger: Sending a request to an authenticated gh-proxy endpoint with a Content-Length header that is not a decimal integer, e.g. 'Content-Length: abc', '10, 20', or a header containing units like '10 bytes'.

Common situations: Misconfigured proxies/clients emitting duplicate or malformed Content-Length; hand-rolled HTTP clients writing the header manually; fuzzing or non-conformant middleware rewriting headers.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/9be666332f468dc6. Report an issue: GitHub.