docker/cli · error
command did not exit after : stderr=
Error message
command %v did not exit after %v: stderr=%q
What it means
Raised by commandConn.handleEOF (commandconn.go:147-151) when the underlying command's stdout/stdin returned io.EOF but the command process did not terminate within 10 seconds of calling cmd.Wait(). This is a watchdog error: the data stream ended yet the process hangs, so the connection cannot be cleanly finalized.
Solutions
- Check network connectivity to the remote host and that the remote Docker daemon is responsive.
- Inspect the embedded stderr (logged at debug level by the stderrWriter) for the actual ssh/remote error.
- Kill stale ssh control sockets / processes (`pkill -f 'ssh.*docker'`).
- Verify Docker 18.09+ is installed on the remote host (required for `docker system dial-stdio`).
- Retry the connection; if persistent, simplify the DOCKER_HOST ssh URL and test ssh manually.
Example fix
# test the underlying ssh command manually to surface the real error ssh -o ConnectTimeout=30 -T <user>@<host> docker --host=unix:///var/run/docker.sock system dial-stdio
Defensive patterns
Strategy: retry
Validate before calling
// Pre-flight: confirm the ssh command can reach a docker daemon before use.
if err := exec.Command("ssh", "-o", "ConnectTimeout=30", "-T", userHost, "docker", "version").Run(); err != nil {
return fmt.Errorf("remote docker unreachable: %w", err)
} Try / catch
// commandConn errors propagate through the http.Client; treat as transient or fatal.
resp, err := httpClient.Do(req)
if err != nil && strings.Contains(err.Error(), "did not exit after") {
// stale ssh process; retry once after cleanup
exec.Command("pkill", "-f", "ssh.*dial-stdio").Run()
} Prevention
- Keep ssh ControlMaster/ControlPath clean; stale sockets cause hangs.
- Ensure the remote Docker daemon is 18.09+ and healthy.
- Set `-o ConnectTimeout=30` (the helper does, but verify your ssh config doesn't override).
When it happens
Trigger: Reading/writing a commandConn (e.g. an ssh tunnel to a remote Docker daemon) yields io.EOF; handleEOF spawns a goroutine calling cmd.Wait() with a 10s timeout. If Wait does not return within 10s, this error is produced including the captured stderr.
Common situations: The remote ssh process is hung (network black-hole, remote Docker daemon wedged, ssh multiplexing control socket stuck), or the remote command blocks waiting on a resource. The remote host may be unreachable mid-stream, or an older/buggy ssh client/server combination leaves a zombie. Also seen when the remote Docker daemon is not 18.09+.
Related errors
- command has exited with , make sure the URL is valid, and…
- timeout waiting for stats
- no scheme provided
- incorrect scheme:
- plain-text password is not supported
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/8d3cb7c8786e5587.
Report an issue: GitHub.
Appendix: source
Thrown at cli/connhelper/commandconn/commandconn.go:151
c.cmdMutex.Lock()
defer c.cmdMutex.Unlock()
var werr error
if c.cmdExited.Load() {
werr = c.cmdWaitErr
} else {
werrCh := make(chan error)
go func() { werrCh <- c.cmd.Wait() }()
select {
case werr = <-werrCh:
c.cmdWaitErr = werr
c.cmdExited.Store(true)
case <-time.After(10 * time.Second):
c.stderrMu.Lock()
stderr := c.stderr.String()
c.stderrMu.Unlock()
return fmt.Errorf("command %v did not exit after %v: stderr=%q", c.cmd.Args, err, stderr)
}
}
if werr == nil {
return err
}
c.stderrMu.Lock()
stderr := c.stderr.String()
c.stderrMu.Unlock()
return fmt.Errorf("command %v has exited with %v, make sure the URL is valid, and Docker 18.09 or later is installed on the remote host: stderr=%s", c.cmd.Args, werr, stderr)
}
func ignorableCloseError(err error) bool {
return strings.Contains(err.Error(), os.ErrClosed.Error())
}
func (c *commandConn) Read(p []byte) (int, error) {
n, err := c.stdout.Read(p)View on GitHub (pinned to 4f84911bfe)