guzzle/guzzle · error · GuzzleHttp\Exception\RequestException

HTTP/3 is not supported by this cURL installation.

Error message

HTTP/3 is not supported by this cURL installation.

What it means

Thrown by getDefaultConf() when the request protocol version is HTTP/3 ('3' or '3.0') but the PHP cURL extension does not define CURL_HTTP_VERSION_3, meaning libcurl was not built with HTTP/3 support. HTTP/3 requires a specially built libcurl with a QUIC/HTTP3 backend (ngtcp2, quiche, etc.).

Source

Thrown at src/Handler/CurlFactory.php:2130

            \CURLOPT_CUSTOMREQUEST => $easy->request->getMethod(),
            \CURLOPT_URL => (string) $uri->withFragment(''),
            \CURLOPT_RETURNTRANSFER => false,
            \CURLOPT_HEADER => false,
            \CURLOPT_CONNECTTIMEOUT_MS => self::DEFAULT_CONNECT_TIMEOUT_MS,
        ];

        if (CurlVersion::supportsProtocolsStr()) {
            $conf[(int) \constant('CURLOPT_PROTOCOLS_STR')] = \implode(',', $protocols);
        } else {
            $conf[\CURLOPT_PROTOCOLS] = self::curlProtocolMask($protocols);
        }

        $version = $easy->request->getProtocolVersion();
        $multiplex = self::normalizeMultiplex($easy->options);

        if ('3' === $version || '3.0' === $version) {
            if (!\defined('CURL_HTTP_VERSION_3')) {
                throw new RequestException('HTTP/3 is not supported by this cURL installation.', $easy->request);
            }

            $proxy = ProxyEnv::resolveProxySelection($easy->request->getUri(), $easy->options['proxy'] ?? null);

            if (\in_array($multiplex, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) {
                self::assertSelectedProxySupported($proxy->getProxy(), $easy->request);

                if ($proxy->hasProxy()) {
                    throw new RequestException('Required multiplexing cannot be guaranteed for HTTP/3 requests sent through a proxy.', $easy->request);
                }
                if (!CurlVersion::supportsRequiredHttp3Multiplex()) {
                    throw new RequestException('Required multiplexing for HTTP/3 needs libcurl 8.13.0 or newer built with HTTP/3 support.', $easy->request);
                }
                // HTTP/3 or fail: required multiplexing never downgrades, not
                // even to HTTP/2.
                $conf[\CURLOPT_HTTP_VERSION] = (int) \constant('CURL_HTTP_VERSION_3ONLY');
            } else {
                $conf[\CURLOPT_HTTP_VERSION] = $proxy->hasProxy()

View on GitHub (pinned to 9b200fc580)

Solutions

  1. Use HTTP/2 ('2' or '2.0') instead, which is widely supported and negotiates with HTTP/3-capable servers via Alt-Svc.
  2. Install a libcurl build with HTTP/3 support (e.g. compiled with ngtcp2/quiche) and rebuild the PHP cURL extension.
  3. Remove the 'version' => '3' option and let Guzzle/libcurl negotiate.

Example fix

// before
$client->get($url, ['version' => '3']);
// after
$client->get($url, ['version' => '2']);
Defensive patterns

Strategy: validation

Validate before calling

if (($options['version'] ?? null) === '3' && !defined('CURL_HTTP_VERSION_3')) {
    throw new \RuntimeException('HTTP/3 not supported by this cURL build; use version 2 or upgrade libcurl.');
}

Type guard

function libcurlSupportsHttp3(): bool { return defined('CURL_HTTP_VERSION_3'); }

Try / catch

try {
    $client->get($url, ['version' => '3']);
} catch (\GuzzleHttp\Exception\RequestException $e) {
    // Fall back to HTTP/2 or upgrade libcurl.
}

Prevention

When it happens

Trigger: Calling $client->get($url, ['version' => '3']) (or 3.0) on a standard libcurl build without HTTP/3 support.

Common situations: Most default libcurl installations lack HTTP/3; requesting version 3 because the server supports it without checking the client; testing HTTP/3 in CI with a stock image.

Related errors


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