composer/composer · error · RuntimeException

Failed getting info from curl handle {i} ({url})

Error message

Failed getting info from curl handle {i} ({url})

What it means

Thrown when curl_getinfo() returns false for a curl easy-handle inside the multi-exec processing loop of CurlDownloader. Composer relies on curl_getinfo to read transfer metadata (HTTP code, size, timing) before rejecting/completing a job; a false return means the underlying libcurl handle is no longer queryable, so Composer cannot continue safely. The message includes the internal handle id ($i) and the sanitized URL so you can identify which request failed. This is a low-level libcurl/PHP interop failure rather than a normal HTTP error.

Source

Thrown at src/Composer/Util/Http/CurlDownloader.php:361

        $active = true;
        $this->checkCurlResult(curl_multi_exec($this->multiHandle, $active));
        if (-1 === curl_multi_select($this->multiHandle, $this->selectTimeout)) {
            // sleep in case select returns -1 as it can happen on old php versions or some platforms where curl does not manage to do the select
            usleep(150);
        }

        while ($progress = curl_multi_info_read($this->multiHandle)) {
            $curlHandle = $progress['handle'];
            $result = $progress['result'];
            $i = (int) $curlHandle;
            if (!isset($this->jobs[$i])) {
                continue;
            }

            $progress = curl_getinfo($curlHandle);
            if (false === $progress) {
                throw new \RuntimeException('Failed getting info from curl handle '.$i.' ('.Url::sanitize($this->jobs[$i]['url']).')');
            }
            $job = $this->jobs[$i];
            unset($this->jobs[$i]);
            $error = curl_error($curlHandle);
            $errno = curl_errno($curlHandle);
            curl_multi_remove_handle($this->multiHandle, $curlHandle);
            if (\PHP_VERSION_ID < 80000) {
                curl_close($curlHandle);
            }

            $headers = null;
            $statusCode = null;
            $response = null;
            try {
                // TODO progress
                if (CURLE_OK !== $errno || $error || $result !== CURLE_OK) {
                    $errno = $errno ?: $result;
                    if (!$error && function_exists('curl_strerror')) {

View on GitHub (pinned to 6ffc117740)

Solutions

  1. Update PHP and the php-curl/libcurl extension to the latest patch release for your PHP version.
  2. Check COMPOSER_CAFILE / openssl CA bundle and proxy env vars (HTTP_PROXY/HTTPS_PROXY) for corruption that could destabilize transfers.
  3. Reproduce with `composer <command> -vvv` to capture the exact URL/handle, then disable parallel downloads via COMPOSER_MAX_PARALLEL_HTTP=1 to rule out multi-handle races.
  4. If it persists on a specific URL, test that URL directly with curl on the host to isolate a libcurl-level fault.

Example fix

// before (env instability)
COMPOSER_MAX_PARALLEL_HTTP=50 composer install
// after (isolate multi-handle races, upgrade path)
COMPOSER_MAX_PARALLEL_HTTP=1 composer install -vvv
Defensive patterns

Strategy: retry

Validate before calling

// Cannot fully prevent (libcurl-internal); validate transport health before the run.
if (!extension_loaded('curl')) { throw new \RuntimeException('php-curl extension required'); }
if (!is_callable('curl_multi_init')) { throw new \RuntimeException('curl multi support missing'); }

Try / catch

try { $response = $httpDownloader->get($url); } catch (\RuntimeException $e) {
    if (str_starts_with($e->getMessage(), 'Failed getting info from curl handle')) {
        // transient libcurl state — retry once after reset, then surface
        $httpDownloader->reset(); $response = $httpDownloader->get($url);
    } else { throw $e; }
}

Prevention

When it happens

Trigger: Occurs in CurlDownloader::select()/loop when curl_multi_info_read yields a finished handle but curl_getinfo on that handle returns false — typically when the handle was already closed, the multi-handle is in a bad state, or libcurl hit an internal error mid-transfer.

Common situations: Hitting this during a `composer install/update` behind a flaky proxy, after a process fork that duplicated curl handles, on PHP versions with libcurl bugs, or when a custom transport/curl option conflicts with Composer's multi-handle. Rare in normal use; usually signals a corrupted curl session or environment (e.g. outdated php-curl extension).

Related errors


AI-assisted analysis of composer/composer@6ffc117740 (2026-08-07). Data as JSON: /api/errors/1461402a359301b0. Report an issue: GitHub.