guzzle/guzzle · error · GuzzleHttp\Exception\RequestException

HTTP/3 is supported by the cURL handler, however the install

Error message

HTTP/3 is supported by the cURL handler, however the installed PHP cURL extension or libcurl does not support HTTP/3.

What it means

Thrown during CurlFactory::create() when the request targets HTTP/3 (protocol version '3' or '3.0') but CurlVersion::supportsHttp3() returns false. HTTP/3 support requires libcurl 7.88.0+ built with the CURL_VERSION_HTTP3 feature bit and the CURL_HTTP_VERSION_3 / CURL_HTTP_VERSION_3ONLY PHP constants. The guard fails fast rather than silently downgrading to HTTP/2 or HTTP/1.1.

Source

Thrown at src/Handler/CurlFactory.php:256

        if ('' === $protocolVersion) {
            throw new RequestException('HTTP protocol version must not be empty.', $request);
        }

        if (1 !== \preg_match('/^\d+(?:\.\d+)?$/D', $protocolVersion)) {
            throw new RequestException('HTTP protocol version must be a valid HTTP version number.', $request);
        }

        CurlVersion::ensureSupported($request);

        $multiplex = self::normalizeMultiplex($options);

        if ('3' === $protocolVersion || '3.0' === $protocolVersion) {
            if (!CurlVersion::supportsHttp3()) {
                if (\in_array($multiplex, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) {
                    throw new RequestException('Required multiplexing for HTTP/3 needs libcurl 8.13.0 or newer built with HTTP/3 support.', $request);
                }

                throw new RequestException('HTTP/3 is supported by the cURL handler, however the installed PHP cURL extension or libcurl does not support HTTP/3.', $request);
            }
        } elseif ('2' === $protocolVersion || '2.0' === $protocolVersion) {
            if (!CurlVersion::supportsHttp2()) {
                if (\in_array($multiplex, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) {
                    throw new RequestException('Required multiplexing needs libcurl 8.14.0 or newer built with HTTP/2 support.', $request);
                }

                throw new RequestException('HTTP/2 is supported by the cURL handler, however libcurl 7.65.2 or newer built with HTTP/2 support is required.', $request);
            }
        } elseif ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) {
            throw new RequestException(sprintf('HTTP/%s is not supported by the cURL handler.', $protocolVersion), $request);
        }

        if (isset($options['curl']['body_as_string'])) {
            $options['_body_as_string'] = $options['curl']['body_as_string'];
            unset($options['curl']['body_as_string']);
        }

View on GitHub (pinned to 9b200fc580)

Solutions

  1. Install or rebuild libcurl with HTTP/3 support (e.g. apt install curl with quiche/ngtcp2, or build from source with --with-openssl-quic), then rebuild the PHP cURL extension against it.
  2. Verify support at runtime: check that CurlVersion::supportsHttp3() (or curl_version()['features'] & CURL_VERSION_HTTP3) is non-zero before sending HTTP/3 requests.
  3. If HTTP/3 is optional, fall back to HTTP/2 ('2.0') or HTTP/1.1 when the feature bit is absent.
  4. Use a Docker image that bundles HTTP/3-capable libcurl (e.g. a custom image built on a distro with a QUIC-enabled curl).

Example fix

// before
$response = $client->send($request->withProtocolVersion('3.0'));

// after
if (GuzzleHttp\Handler\CurlVersion::supportsHttp3()) {
    $request = $request->withProtocolVersion('3.0');
} else {
    $request = $request->withProtocolVersion('2.0');
}
$response = $client->send($request);
Defensive patterns

Strategy: validation

Validate before calling

use GuzzleHttp\Handler\CurlVersion;
if (!CurlVersion::supportsHttp3()) {
    // do not request version '3.0'; fall back to HTTP/2 or 1.1
    $version = CurlVersion::supportsHttp2() ? '2.0' : '1.1';
} else {
    $version = '3.0';
}
$request = $request->withProtocolVersion($version);

Type guard

// HTTP/3 is a runtime capability, not a type; check the feature bit.
function supportsHttp3(): bool {
    return \defined('CURL_VERSION_HTTP3')
        && \defined('CURL_HTTP_VERSION_3')
        && \defined('CURL_HTTP_VERSION_3ONLY')
        && (curl_version()['features'] & \CURL_VERSION_HTTP3) !== 0;
}

Prevention

When it happens

Trigger: Calling the handler with a request whose protocol version is '3' or '3.0' (e.g. $request->withProtocolVersion('3.0')) on a host whose libcurl lacks the CURL_VERSION_HTTP3 feature. Also reached if CURLOPT_HTTP_VERSION is set to CURL_HTTP_VERSION_3 via curl options while the version string is '3'.

Common situations: Default Debian/Ubuntu/RHEL PHP packages ship libcurl without HTTP/3 (no QUIC/ngtcp2). Alpine musl builds and most Docker PHP base images also lack it. A developer enables HTTP/3 after reading docs but tests on a CI runner or local machine that was never rebuilt with --with-openssl-quic or --with-ngtcp2/nghttp3.

Related errors


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