guzzle/guzzle · error · RequestException
HTTP/3 is supported by the cURL handler, however the…
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 when a request pins HTTP/3 (protocol version '3' or '3.0') but the installed PHP cURL extension or libcurl cannot do HTTP/3 (no CURL_VERSION_HTTP3 feature bit, libcurl older than 7.88.0, or the CURL_HTTP_VERSION_3/3ONLY constants are absent). Guzzle pins HTTP/3 via CURL_HTTP_VERSION_3ONLY so it needs dependable support rather than an opportunistic upgrade, hence it fails the request with a RequestException. Only reached when multiplex is NOT set to REQUIRE_EAGER/REQUIRE_WAIT (that combination throws a different HTTP/3 message at line 253).
Solutions
- Rebuild libcurl with an HTTP/3 backend (quiche, ngtcp2+nghttp3, or openssl-quic) and rebuild ext-curl against it; verify with (curl_version()['features'] & CURL_VERSION_HTTP3) !== 0.
- Downgrade the request to HTTP/2 ('2') or HTTP/1.1 so ALPN negotiation handles it without HTTP/3.
- Fall back to StreamHandler if HTTP/3 is optional and you only need the call to succeed.
Example fix
// before
$request = $request->withProtocolVersion('3');
// after
$request = $request->withProtocolVersion('2'); Defensive patterns
Strategy: validation
Validate before calling
$v = function_exists('curl_version') ? curl_version() : false;
$supportsHttp3 = is_array($v)
&& defined('CURL_VERSION_HTTP3')
&& defined('CURL_HTTP_VERSION_3')
&& defined('CURL_HTTP_VERSION_3ONLY')
&& version_compare($v['version'], '7.88.0', '>=')
&& (((int) constant('CURL_VERSION_HTTP3')) & $v['features']) !== 0;
if (!$supportsHttp3 && in_array($request->getProtocolVersion(), ['3', '3.0'], true)) {
$request = $request->withProtocolVersion('2');
} Try / catch
try {
$response = $client->send($request);
} catch (\GuzzleHttp\Exception\RequestException $e) {
if (str_contains($e->getMessage(), 'does not support HTTP/3')) {
// retry with an HTTP/2 or HTTP/1.1 request
}
throw $e;
} Prevention
- Gate HTTP/3 usage behind a curl_version() feature check at boot.
- Document the deployment's required libcurl build (HTTP/3 backend) next to the client setup.
When it happens
Trigger: A request built with ->withProtocolVersion('3') (or '3.0'), sent through CurlHandler/CurlMultiHandler, where curl_version()['features'] lacks the CURL_VERSION_HTTP3 bit or libcurl < 7.88.0. The 'multiplex' option must be absent, null, or a non-REQUIRE value.
Common situations: Default Debian/Ubuntu libcurl ships without an HTTP/3 backend (ngtcp2/quiche). Alpine's curl package historically lacks HTTP/3. CI images pinned to older curl. A dev who tested on a quiche-enabled build then deployed to a minimal container.
Related errors
- HTTP/2 is supported by the cURL handler, however libcurl…
- Required multiplexing for HTTP/3 needs libcurl 8.13.0 or…
- The configured cURL share handle requires CURLOPT_SHARE…
- cURL or higher is required by the cURL handler; is…
- HTTPS proxies are not supported by the installed libcurl…
AI-assisted analysis of guzzle/guzzle@d1cbca7697 (2026-08-06).
Data as JSON: /api/errors/d8695bf0bdd9e69f.
Report an issue: GitHub.
Appendix: 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 d1cbca7697)