guzzle/guzzle · error · GuzzleHttp\Exception\InvalidArgumentException

The "multiplex" request option cannot be Multiplexing::NONE

Error message

The "multiplex" request option cannot be Multiplexing::NONE on a CurlMultiHandler that permits multiplexing and requires persistent transport sharing; set the "multiplex" client or CurlMultiHandler constructor option to Multiplexing::NONE, or use TransportSharing::PERSISTENT_PREFER.

What it means

Thrown by CurlMultiHandler::applyMultiplexNone() when the per-request multiplex option is NONE on a multiplexing handler, the request is HTTP/1.x, but the handler's transport sharing mode is TransportSharing::PERSISTENT_REQUIRE. Enforcing NONE on older libcurl (without reliable reuse matching) forces a fresh connection, which persistent-require sharing forbids because it disables reuse. The two requirements are incompatible, so it fails closed.

Source

Thrown at src/Handler/CurlMultiHandler.php:432

            return;
        }

        if (!$this->ownsFactory) {
            throw new InvalidArgumentException('The "multiplex" request option can only be Multiplexing::NONE on a CurlMultiHandler with a custom "handle_factory" when the handler\'s own "multiplex" option is Multiplexing::NONE, because the guarantee is enforced against the native easy handle the factory controls.');
        }

        $version = $easy->request->getProtocolVersion();
        if ('1.0' !== $version && '1.1' !== $version) {
            throw new InvalidArgumentException('The "multiplex" request option can only be Multiplexing::NONE for an HTTP/1.x request on a CurlMultiHandler that permits multiplexing; set the "multiplex" client or CurlMultiHandler constructor option to Multiplexing::NONE to disable multiplexing for every transfer, or send the request with its "version" option set to "1.1".');
        }

        if (null !== $this->shareHandleState && TransportSharing::PERSISTENT_REQUIRE === $this->shareHandleState->mode) {
            // The matcher-window hardening below may force a fresh
            // connection, which required persistent sharing forbids because
            // it disables connection reuse (CurlFactory applies the same
            // rule to the raw option); rejected on every runtime so
            // acceptance stays version-independent.
            throw new InvalidArgumentException('The "multiplex" request option cannot be Multiplexing::NONE on a CurlMultiHandler that permits multiplexing and requires persistent transport sharing; set the "multiplex" client or CurlMultiHandler constructor option to Multiplexing::NONE, or use TransportSharing::PERSISTENT_PREFER.');
        }

        if (\defined('CURLOPT_HTTPAUTH')
            && isset($options['curl'])
            && \is_array($options['curl'])
            && \array_key_exists((int) \constant('CURLOPT_HTTPAUTH'), $options['curl'])
        ) {
            // Key presence alone conflicts: challenge-response retries are
            // libcurl-internal follows, which disarm CURLOPT_FRESH_CONNECT
            // (reuse_fresh && !this_is_a_follow), so the hardening below
            // cannot cover them.
            throw new InvalidArgumentException('The "multiplex" request option cannot be Multiplexing::NONE combined with the raw CURLOPT_HTTPAUTH cURL option on a CurlMultiHandler that permits multiplexing; remove the raw option, or set the "multiplex" client or CurlMultiHandler constructor option to Multiplexing::NONE.');
        }

        if (Psr7\Utils::caselessContains($easy->request->getHeaderLine('Expect'), '100-continue')) {
            // libcurl arms its Expect handling by a caseless substring scan
            // of the header value (Curl_compareheader), so any value
            // containing 100-continue can make a 417 response retry as an

View on GitHub (pinned to 9b200fc580)

Solutions

  1. Switch sharing to TransportSharing::PERSISTENT_PREFER, which does not forbid fresh connections.
  2. Disable multiplexing globally (handler/client multiplex => NONE) so NONE does not need hardening.
  3. Drop the per-request NONE for that transfer.

Example fix

// before
new CurlMultiHandler(['transport_sharing' => TransportSharing::PERSISTENT_REQUIRE]);
$client->get($url, ['multiplex' => Multiplexing::NONE]);
// after
new CurlMultiHandler(['transport_sharing' => TransportSharing::PERSISTENT_PREFER]);
$client->get($url, ['multiplex' => Multiplexing::NONE]);
Defensive patterns

Strategy: validation

Validate before calling

use GuzzleHttp\TransportSharing;
if (($handlerOptions['transport_sharing'] ?? null) === TransportSharing::PERSISTENT_REQUIRE
    && ($requestOptions['multiplex'] ?? null) === GuzzleHttp\Multiplexing::NONE
) {
    throw new InvalidArgumentException('PERSISTENT_REQUIRE sharing forbids the fresh-connection hardening that NONE may need');
}

Prevention

When it happens

Trigger: Handler built with transport_sharing => TransportSharing::PERSISTENT_REQUIRE (default multiplexing on); request with multiplex => NONE on libcurl below 7.77.0 (or 8.11.0-8.12.1) where the hardening would force a fresh connection.

Common situations: Requiring persistent connection pooling for throughput while trying to isolate a single sensitive transfer; the requirement and the isolation contradict.

Related errors


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