php-amqplib/php-amqplib · critical · AMQPConnectionClosedException

$code

$code

Error message

socket_strerror($code)

What it means

In SocketIO::write(), when socket_write() raises an ErrorException, the code maps the errno: connection-fatal codes (EPIPE, ENETDOWN, ENETUNREACH, ENETRESET, ECONNABORTED, ECONNRESET, ECONNREFUSED, ETIMEDOUT) close the socket and throw AMQPConnectionClosedException whose message is socket_strerror($code) — the raw OS error text (e.g. 'Broken pipe'). This is the transport-level signal that the TCP connection is gone.

Solutions

  1. Catch AMQPConnectionClosedException and reconnect ($connection->reconnect(), re-open channels) before retrying the publish.
  2. Enable client heartbeats and keepalive so dead TCP connections are detected and re-established proactively.
  3. Check the broker is reachable and its advertised host/port/firewall rules allow sustained connections.
  4. Use publisher confirms / message persistence so messages lost during connection drops can be resent.

Example fix

// before
$channel->basic_publish($msg, 'ex');

// after
try {
    $channel->basic_publish($msg, 'ex');
} catch (PhpAmqpLib\Exception\AMQPConnectionClosedException $e) {
    $connection->reconnect();
    $channel = $connection->channel();
    $channel->basic_publish($msg, 'ex');
}
Defensive patterns

Strategy: retry

Validate before calling

if (!$connection->isConnected()) { $connection->reconnect(); $channel = $connection->channel(); }

Try / catch

try {
    $channel->basic_publish($msg, 'ex');
} catch (\PhpAmqpLib\Exception\AMQPConnectionClosedException $e) {
    $connection->reconnect();
    $channel = $connection->channel();
    $channel->basic_publish($msg, 'ex');
}

Prevention

When it happens

Trigger: Publishing or sending any frame when the peer has reset or closed the TCP connection: broker restart, ECONNREFUSED after broker shutdown, broken pipe after broker closed the socket, ETIMEDOUT on a dead network path.

Common situations: Long-running RabbitMQ consumer losing the broker (restart, failover, HA promotion); network partition or VPN drop; broker closing idle connections because heartbeats are disabled or too large; Docker/k8s restarting the RabbitMQ container under a producer.

Related errors


AI-assisted analysis of php-amqplib/php-amqplib@381b6f7c60 (2026-09-20). Data as JSON: /api/errors/70987d40b0125c37. Report an issue: GitHub.

Appendix: source

Thrown at PhpAmqpLib/Wire/IO/SocketIO.php:214

                        $buffer = mb_substr($data, $written, self::BUFFER_SIZE, 'ASCII');
                    }
                    $result = socket_write($this->sock, $buffer);
                }
                $this->throwOnError();
            } catch (\ErrorException $e) {
                $code = socket_last_error($this->sock);
                $constants = SocketConstants::getInstance();
                switch ($code) {
                    case $constants->SOCKET_EPIPE:
                    case $constants->SOCKET_ENETDOWN:
                    case $constants->SOCKET_ENETUNREACH:
                    case $constants->SOCKET_ENETRESET:
                    case $constants->SOCKET_ECONNABORTED:
                    case $constants->SOCKET_ECONNRESET:
                    case $constants->SOCKET_ECONNREFUSED:
                    case $constants->SOCKET_ETIMEDOUT:
                        $this->close();
                        throw new AMQPConnectionClosedException(socket_strerror($code), $code, $e);
                    default:
                        throw new AMQPIOException(sprintf(
                            'Error sending data. Last SocketError: %s',
                            socket_strerror($code)
                        ), $code, $e);
                }
            } finally {
                $this->restoreErrorHandler();
            }

            if ($result === false) {
                throw new AMQPIOException(sprintf(
                    'Error sending data. Last SocketError: %s',
                    socket_strerror(socket_last_error($this->sock))
                ));
            }

            $now = microtime(true);

View on GitHub (pinned to 381b6f7c60)