guzzle/guzzle · error · RuntimeException

value is not a non-negative decimal integer

Error message

value is not a non-negative decimal integer

What it means

Thrown by HeaderProcessor::parseContentLength() when a Content-Length value, after trimming spaces/tabs and splitting on commas, does not match ^[0-9]+$. Content-Length must be a single non-negative decimal integer; signed, decimal, hex, or empty values are rejected per RFC 7230. The same validator runs for both request and response Content-Length.

Solutions

  1. Inspect the actual Content-Length header on the wire (curl -v, on_headers) to see the malformed value.
  2. Remove any manually set Content-Length on the request and let Guzzle compute it from the body size.
  3. Report the malformed header to the server/CDN operator.
  4. Catch RuntimeException when you must interoperate with the broken endpoint.

Example fix

// before - manually forcing a bad length
$request = new \GuzzleHttp\Psr7\Request('POST', $url, ['Content-Length' => '1.5'], $body);

// after - let Guzzle derive the length
$request = new \GuzzleHttp\Psr7\Request('POST', $url, [], $body);
Defensive patterns

Strategy: try-catch

Validate before calling

// For request-side Content-Length, never set it manually; let Guzzle compute it.
// If you must set it, validate first:
if ($cl !== null && preg_match('/^[0-9]+$/D', (string) $cl) !== 1) {
    throw new \InvalidArgumentException('Bad Content-Length');
}

Type guard

function isValidContentLength(?string $value): bool {
    return $value === null || preg_match('/^[0-9]+$/D', $value) === 1;
}

Try / catch

try {
    $response = $client->get($url);
} catch (\RuntimeException $e) {
    if (str_contains($e->getMessage(), 'not a non-negative decimal integer')) {
        // surface the bad header value to logs
    }
    throw $e;
}

Prevention

When it happens

Trigger: A server sends 'Content-Length: 1.5', 'Content-Length: -5', 'Content-Length: 0x10', 'Content-Length:' (empty), or 'Content-Length: 1024;' (trailing semicolon). Also triggered on the request side when a caller sets Content-Length to a non-integer string.

Common situations: Origin server bug, a CDN rewriting bodies but leaving a stale/decimal length, custom middleware setting Content-Length to a computed float, or proxy concatenation producing malformed values.

Related errors


AI-assisted analysis of guzzle/guzzle@d1cbca7697 (2026-08-06). Data as JSON: /api/errors/7be6800f840fd28c. Report an issue: GitHub.

Appendix: source

Thrown at src/Handler/HeaderProcessor.php:114

        return \preg_match('/^[\x20\x09\x21-\x7E\x80-\xFF]*(?:\r\n|\r|\n)?$/D', \trim($parts[1], " \t")) === 1;
    }

    /**
     * Returns a normalized decimal Content-Length, or null when absent.
     *
     * @param string[] $values
     *
     * @throws \RuntimeException when Content-Length is malformed or conflicting.
     */
    public static function parseContentLength(array $values): ?string
    {
        $length = null;

        foreach ($values as $value) {
            foreach (\explode(',', $value) as $part) {
                $part = \trim($part, " \t");
                if (\preg_match('/^[0-9]+$/D', $part) !== 1) {
                    throw new \RuntimeException('value is not a non-negative decimal integer');
                }

                $part = \ltrim($part, '0');
                $part = $part === '' ? '0' : $part;
                if ($length !== null && $part !== $length) {
                    throw new \RuntimeException('values conflict');
                }

                $length = $part;
            }
        }

        if ($length === null) {
            return null;
        }

        return $length;
    }

View on GitHub (pinned to d1cbca7697)