guzzle/guzzle · error · GuzzleHttp\Exception\ResponseTransferException

Failed while transferring the response body

Error message

Failed while transferring the response body

What it means

Thrown by StreamHandler::drain() when copying the response body fails with any Exception that is not a ResponseException, TimeoutException, or OverflowException (those are handled earlier). It is wrapped as a ResponseTransferException using the original $e->getMessage() when non-empty, else the fallback 'Failed while transferring the response body'. It carries the request, response, and the underlying exception.

Solutions

  1. Inspect the previous exception ($e->getPrevious()) for the real transport error and address that (TLS, connectivity, corrupt encoding).
  2. Retry idempotent requests; the response is partial and unusable.
  3. For large bodies, stream to disk and enable retry with Range/offset to resume.

Example fix

// before
try { $client->get($u); }
catch (\GuzzleHttp\Exception\ResponseTransferException $e) { /* generic, lose detail */ }
// after
try { $client->get($u); }
catch (\GuzzleHttp\Exception\ResponseTransferException $e) {
    $cause = $e->getPrevious() ? $e->getPrevious()->getMessage() : $e->getMessage();
    $logger->error('body transfer failed: '.$cause); // act on the real cause
}
Defensive patterns

Strategy: retry

Try / catch

try {
    $resp = $client->get($u);
} catch (\GuzzleHttp\Exception\ResponseTransferException $e) {
    $cause = $e->getPrevious();
    // only retry idempotent methods; log the underlying cause for diagnosis
    if (in_array($method, ['GET','HEAD','PUT','DELETE'], true)) {
        $resp = $client->get($u);
    }
}

Prevention

When it happens

Trigger: An I/O failure mid-body that is not a timeout: the transport stream read() throws a RuntimeException (e.g. SSL connection reset not matched to the network-error list, broken pipe, filter error), or a dechunk/inflate filter raises. The generic catch is at src/Handler/StreamHandler.php:707.

Common situations: Connection reset by peer while reading the body; SSL errors during body transfer; zlib inflate failure on a corrupt gzip body; broken pipe from an intermediary proxy; intermittent network errors.

Related errors


AI-assisted analysis of guzzle/guzzle@d1cbca7697 (2026-08-06). Data as JSON: /api/errors/71c467f405249553. Report an issue: GitHub.

Appendix: source

Thrown at src/Handler/StreamHandler.php:710

                // that number of bytes has been read. This can prevent infinitely
                // reading from a stream when dealing with servers that do not
                // honor Connection: Close headers.
                $copied = Psr7\Utils::copyToStream($source, $target, $copyLimit);
            } catch (ResponseException $e) {
                throw $e;
            } catch (TimeoutException $e) {
                throw new ResponseTimeoutException(
                    'Timed out while transferring the response body',
                    $request,
                    $response,
                    $e
                );
            } catch (\OverflowException $e) {
                throw new ResponseException($e->getMessage(), $request, $response, $e);
            } catch (\Exception $e) {
                // Any other response-body transfer failure surfaces as a
                // ResponseTransferException carrying the response.
                throw new ResponseTransferException(
                    $e->getMessage() !== '' ? $e->getMessage() : 'Failed while transferring the response body',
                    $request,
                    $response,
                    $e
                );
            }

            $receivedLength = $encodedBody !== null ? $encodedBody->getBytesRead() : $copied;
            if ($declaredLength !== null && $receivedLength < $declaredLength) {
                throw new ResponseTransferException(
                    'Response body ended before the declared Content-Length was reached',
                    $request,
                    $response
                );
            }

            try {
                if ($sink->isSeekable()) {

View on GitHub (pinned to d1cbca7697)