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

  1. Check network connectivity to the remote host and that the remote Docker daemon is responsive.
  2. Inspect the embedded stderr (logged at debug level by the stderrWriter) for the actual ssh/remote error.
  3. Kill stale ssh control sockets / processes (`pkill -f 'ssh.*docker'`).
  4. Verify Docker 18.09+ is installed on the remote host (required for `docker system dial-stdio`).
  5. 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

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


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)