guzzle/guzzle · error · GuzzleHttp\Exception\RequestException
The stream handler cannot guarantee a multiplexed protocol…
Error message
The stream handler cannot guarantee a multiplexed protocol; required multiplexing needs a cURL handler.
What it means
Thrown by StreamHandler::__invoke when the 'multiplex' option is Multiplexing::REQUIRE_EAGER or REQUIRE_WAIT. PHP's HTTP stream wrapper speaks only HTTP/1.x (one request per connection), so it can never provide a multiplexed protocol; a hard requirement for multiplexing cannot be satisfied and the request is rejected up front. It is a RequestException (not InvalidArgumentException) because the option value is valid but the handler cannot honor it.
Solutions
- Install and enable ext-curl so Guzzle selects a cURL-based handler that supports multiplexed protocols.
- Drop the REQUIRE_* constraint: use Multiplexing::EAGER/WAIT (advisory) or NONE, which the stream handler accepts.
- Explicitly build the client with a CurlMultiHandler/CurlHandler when REQUIRE_* multiplexing is mandatory.
Example fix
// before $client = new Client(['handler' => new StreamHandler()]); $client->get($u, ['multiplex' => Multiplexing::REQUIRE_EAGER]); // after $client = new Client(['handler' => new CurlMultiHandler()]); $client->get($u, ['multiplex' => Multiplexing::REQUIRE_EAGER]);
Defensive patterns
Strategy: fallback
Validate before calling
use GuzzleHttp\Multiplexing;
$needsMultiplex = in_array($opts['multiplex'] ?? null, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true);
if ($needsMultiplex && !extension_loaded('curl')) {
throw new \RuntimeException('REQUIRE_* multiplexing needs ext-curl; cannot use StreamHandler');
} Try / catch
try {
$client->send($req, ['multiplex' => Multiplexing::REQUIRE_EAGER]);
} catch (\GuzzleHttp\Exception\RequestException $e) {
if (str_contains($e->getMessage(), 'cannot guarantee a multiplexed protocol')) {
// fall back to advisory mode or a cURL handler
}
} Prevention
- Ensure ext-curl is installed when multiplexing (HTTP/2) is required.
- Use advisory Multiplexing::EAGER/WAIT rather than REQUIRE_* unless mandatory.
- Select the handler based on required protocol capabilities.
When it happens
Trigger: Constructing a client on StreamHandler (or using Guzzle's default StreamHandler fallback when ext-curl is absent) and then passing 'multiplex' => Multiplexing::REQUIRE_EAGER / REQUIRE_WAIT on a request or as client config. The check is at src/Handler/StreamHandler.php:173.
Common situations: App config that mandates HTTP/2 multiplexing deployed on a host without ext-curl; shared client builder that forces REQUIRE_* multiplexing regardless of the underlying handler; switching handler at runtime from CurlHandler to StreamHandler.
Related errors
- HTTP/ is not supported by the stream handler.
- Required multiplexing needs libcurl 8.14.0 or newer built…
- The "multiplex" option must be null or a…
- The "multiplex" option must be null or a…
- $e->getMessage()
AI-assisted analysis of guzzle/guzzle@d1cbca7697 (2026-08-06).
Data as JSON: /api/errors/2914202916a58396.
Report an issue: GitHub.
Appendix: source
Thrown at src/Handler/StreamHandler.php:174
// Sleep if there is a delay specified.
if (isset($options['delay'])) {
\usleep((int) ($options['delay'] * 1000));
}
$multiplex = $options['multiplex'] ?? null;
// Multiplexing::NONE is trivially satisfied: the stream handler sends
// one HTTP/1.x request per connection and never multiplexes.
if (null !== $multiplex && !\in_array($multiplex, [Multiplexing::NONE, Multiplexing::EAGER, Multiplexing::WAIT, Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) {
throw new InvalidArgumentException(\sprintf(
'The "multiplex" option must be null or a GuzzleHttp\\Multiplexing::* constant; received %s.',
\get_debug_type($multiplex)
));
}
if (\in_array($multiplex, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true)) {
throw new RequestException('The stream handler cannot guarantee a multiplexed protocol; required multiplexing needs a cURL handler.', $request);
}
$protocolVersion = $request->getProtocolVersion();
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);
}
if ('1.0' !== $protocolVersion && '1.1' !== $protocolVersion) {
throw new RequestException(sprintf('HTTP/%s is not supported by the stream handler.', $protocolVersion), $request);
}
if (isset($options['on_stats']) && !\is_callable($options['on_stats'])) {
throw new InvalidArgumentException('on_stats must be callable');View on GitHub (pinned to d1cbca7697)