guzzle/guzzle · error · \RuntimeException

A response must not contain both Content-Length and Transfer

Error message

A response must not contain both Content-Length and Transfer-Encoding

What it means

Thrown by HeaderProcessor::validateResponseFraming() when a response that can carry a body has both a Content-Length and a Transfer-Encoding header. RFC 7230 forbids the combination and it is a classic request/response-smuggling signal, so Guzzle rejects it outright. \RuntimeException.

Source

Thrown at src/Handler/HeaderProcessor.php:188

        string $method,
        int $status,
        array $headers
    ): ?string {
        if (!self::responseCanHaveBody($method, $status)) {
            return null;
        }

        $normalizedKeys = Utils::normalizeHeaderKeys($headers);
        $contentLength = self::removeHeader('Content-Length', $headers);

        try {
            $length = self::parseContentLength($contentLength);
        } catch (\RuntimeException $e) {
            throw new \RuntimeException('Invalid Content-Length response header: '.$e->getMessage(), 0, $e);
        }

        if ($length !== null && isset($normalizedKeys['transfer-encoding'])) {
            throw new \RuntimeException('A response must not contain both Content-Length and Transfer-Encoding');
        }

        return $length;
    }

    /**
     * Removes every case-insensitive occurrence of a header and returns all
     * removed values in their original field order.
     *
     * @param array<string, string[]> $headers
     *
     * @return string[] Removed values across all header-name casings
     */
    public static function removeHeader(string $name, array &$headers): array
    {
        $values = [];

        foreach ($headers as $key => $headerValues) {

View on GitHub (pinned to 9b200fc580)

Solutions

  1. Inspect the response headers for the simultaneous CL + TE pair
  2. Fix the intermediary so it strips one (chunked responses should not carry Content-Length)
  3. try/catch the request to handle the framing conflict defensively
Defensive patterns

Strategy: try-catch

Try / catch

try {
    $response = $client->request('GET', $url);
} catch (\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'both Content-Length and Transfer-Encoding')) {
        // framing conflict — fix the origin/proxy that emits both
    }
}

Prevention

When it happens

Trigger: The origin or a proxy chain sends both 'Content-Length' and 'Transfer-Encoding' on the same response (e.g. 'Transfer-Encoding: chunked' plus a stale Content-Length).

Common situations: Misconfigured reverse proxy/CDN that forwards both headers, an older intermediary, or a deliberate smuggling attempt.

Related errors


AI-assisted analysis of guzzle/guzzle@9b200fc580 (2026-08-04). Data as JSON: /data/errors/ea4fdeb2c2fa6021.json. Report an issue: GitHub.