phpseclib/phpseclib · error · ConnectionClosedException
Connection closed attempting to forward data to SSH agent
Error message
Connection closed attempting to forward data to SSH agent
What it means
forwardData() relays agent protocol frames received over an SSH channel to the local ssh-agent socket. If fwrite fails to write the complete buffered frame to the agent socket, ConnectionClosedException is thrown indicating the agent connection dropped mid-forwarding.
Solutions
- Confirm ssh-agent is still running and responsive (ssh-add -l from the same environment).
- Catch ConnectionClosedException around SSH operations that trigger forwarding and recreate the Agent to re-open the socket.
- Restart the SSH session/forwarding channel after the agent connection is re-established.
- Check for local firewalls/AV or ulimits killing long-lived unix sockets.
- Reduce idle time between forwarded requests to avoid stale sockets.
Example fix
// before
$data = $agent->forwardData($channelData); // throws if agent socket died
// after
try {
$data = $agent->forwardData($channelData);
} catch (\phpseclib4\Exception\ConnectionClosedException $e) {
$agent = new \phpseclib4\System\SSH\Agent(); // reconnect to agent
$data = null; // drop this frame; client can retry
} Defensive patterns
Strategy: try-catch
Try / catch
try {
$reply = $agent->forwardData($data);
} catch (\phpseclib4\Exception\ConnectionClosedException $e) {
$agent = new \phpseclib4\System\SSH\Agent(); // reopen agent socket
// signal the forwarding channel to retry the request
} Prevention
- Keep ssh-agent running and supervise it (systemd/Windows service auto-restart).
- Avoid long idle forwarding sessions that let sockets go stale.
- Catch ConnectionClosedException at the forwarding boundary and reconnect.
- Watch ulimits/AV software that may kill unix socket connections.
When it happens
Trigger: An SSH client on the remote side is using agent forwarding and sends a signing request; the local agent socket ($this->fsock) is closed or errors while forwardData writes the accumulated socket_buffer to it.
Common situations: ssh-agent exited or restarted during an active forwarded session; timeout killed the local unix socket connection; too many concurrent forwarded requests exhausting the agent.
Understand the failure class
- Connection failures: ECONNREFUSED, ECONNRESET, and friends — why connections get refused, reset, or dropped.
Related errors
- Connection closed while requesting identities
- Connection closed during signing
- \fread() failed.
- {unable_to_write_bytes|only_partial_sent}
- Address is not formatted as a named pipe should be
AI-assisted analysis of phpseclib/phpseclib@1da055d918 (2026-09-18).
Data as JSON: /api/errors/d08ee4286065b012.
Report an issue: GitHub.
Appendix: source
Thrown at phpseclib/System/SSH/Agent.php:271
* Forward data to SSH Agent and return data reply
*/
public function forwardData(string $data): ?string
{
if ($this->expected_bytes > 0) {
$this->socket_buffer .= $data;
$this->expected_bytes -= strlen($data);
} else {
$agent_data_bytes = current(unpack('N', $data));
$current_data_bytes = strlen($data);
$this->socket_buffer = $data;
if ($current_data_bytes != $agent_data_bytes + 4) {
$this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes;
return null;
}
}
if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) {
throw new ConnectionClosedException('Connection closed attempting to forward data to SSH agent');
}
$this->socket_buffer = '';
$this->expected_bytes = 0;
$agent_reply_bytes = current(unpack('N', $this->readBytes(4)));
$agent_reply_data = $this->readBytes($agent_reply_bytes);
$agent_reply_data = current(unpack('a*', $agent_reply_data));
return pack('Na*', $agent_reply_bytes, $agent_reply_data);
}
}
View on GitHub (pinned to 1da055d918)