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

  1. Install and enable ext-curl so Guzzle selects a cURL-based handler that supports multiplexed protocols.
  2. Drop the REQUIRE_* constraint: use Multiplexing::EAGER/WAIT (advisory) or NONE, which the stream handler accepts.
  3. 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

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


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)