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
- 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.
- Verify support at runtime: check that CurlVersion::supportsHttp3() (or curl_version()['features'] & CURL_VERSION_HTTP3) is non-zero before sending HTTP/3 requests.
- If HTTP/3 is optional, fall back to HTTP/2 ('2.0') or HTTP/1.1 when the feature bit is absent.
- 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
- Check CurlVersion::supportsHttp3() before pinning protocol version '3.0'.
- Document the libcurl build requirement (HTTP/3/QUIC) in deployment notes.
- Use feature-detection, not version-string sniffing, for HTTP/3 support.
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
- Required multiplexing for HTTP/3 needs libcurl 8.13.0 or new
- HTTP/2 is supported by the cURL handler, however libcurl 7.6
- HTTP/3 is not supported by this cURL installation.
- Required multiplexing needs libcurl 8.14.0 or newer built wi
- The configured cURL share handle requires CURLOPT_SHARE, but
AI-assisted analysis of guzzle/guzzle@9b200fc580 (2026-08-04).
Data as JSON: /data/errors/d8695bf0bdd9e69f.json.
Report an issue: GitHub.