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. The stream handler speaks only HTTP/1.x (one request per connection) and can never provide a multiplexed protocol, so a hard requirement for multiplexing cannot be satisfied and is rejected before any I/O rather than silently degrading.

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 9b200fc580)

Solutions

  1. Use a cURL-based handler (CurlMultiHandler) which supports HTTP/2 multiplexing for REQUIRE_EAGER/REQUIRE_WAIT.
  2. Downgrade the requirement to Multiplexing::EAGER or WAIT if multiplexing is preferred but not mandatory.
  3. Ensure the ext-curl extension is installed so the default handler is cURL, not the stream handler.

Example fix

// before
$handler = new StreamHandler();
$client->request('GET', $url, ['multiplex' => Multiplexing::REQUIRE_EAGER]);
// after
$handler = HandlerStack::create(); // uses CurlMultiHandler when ext-curl is present
$client->request('GET', $url, ['multiplex' => Multiplexing::REQUIRE_EAGER]);
Defensive patterns

Strategy: fallback

Validate before calling

// Choose the handler based on whether multiplexing is required.
$needMultiplex = in_array($reqOptions['multiplex'] ?? null, [Multiplexing::REQUIRE_EAGER, Multiplexing::REQUIRE_WAIT], true);
$handler = $needMultiplex && function_exists('curl_init')
    ? new CurlMultiHandler()
    : new StreamHandler();

Prevention

When it happens

Trigger: Configuring the client/handler with a StreamHandler (or falling back to it) while a request sets 'multiplex' => Multiplexing::REQUIRE_EAGER or REQUIRE_WAIT to demand an HTTP/2 connection.

Common situations: Defaulting to StreamHandler on systems without the cURL extension; building a handler stack that prefers cURL but falls back to StreamHandler while requests demand multiplexing; misreading REQUIRE_* semantics.

Related errors


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