docker/cli · error

command has exited with , make sure the URL is valid, and…

Error message

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

What it means

Raised by commandConn.handleEOF (commandconn.go:155-161) when io.EOF was received on the stream, cmd.Wait() returned within 10s, and Wait returned a non-nil error. The message includes the command args, the wait error, and captured stderr, and explicitly reminds the user the remote host needs Docker 18.09+ with a valid URL.

Solutions

  1. Read the embedded stderr in the error — it names the real cause (e.g. 'docker: command not found', 'permission denied').
  2. Ensure Docker 18.09+ is installed on the remote host and the docker CLI is on PATH for non-interactive ssh.
  3. Confirm the remote docker.sock path matches the DOCKER_HOST ssh URL (default /var/run/docker.sock).
  4. Verify ssh authentication works non-interactively: `ssh <user>@<host> docker version`.
  5. If the remote daemon needs root/group permissions, ensure the ssh user is in the docker group.

Example fix

# before — wrong socket path / missing docker on remote
export DOCKER_HOST=ssh://user@host
# after — verify docker present and socket path
cmd
ssh user@host 'docker version; ls -l /var/run/docker.sock'
# then set the correct path in the URL if needed
export DOCKER_HOST=ssh://user@host/var/run/docker.sock
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight: confirm docker CLI and socket exist on the remote host.
cmd := exec.Command("ssh", "-o", "ConnectTimeout=30", "-T", userHost, "sh", "-c", "command -v docker && test -S /var/run/docker.sock")
if out, err := cmd.CombinedOutput(); err != nil {
    return fmt.Errorf("remote docker not ready: %v: %s", err, out)
}

Try / catch

conn, err := helper.Dialer(ctx, "tcp", "")
if err != nil {
    return fmt.Errorf("remote docker dial failed (need Docker 18.09+?): %w", err)
}

Prevention

When it happens

Trigger: A commandConn backing an HTTP transport (typically `ssh ... docker system dial-stdio`) exits non-zero. handleEOF wraps the wait error and stderr. The original failure is usually visible in stderr — e.g. `docker: command not found`, `Cannot connect to the Docker daemon`, or ssh auth failure.

Common situations: Remote host lacks Docker or has it not in PATH for the non-login ssh session; the remote dockerd socket path is wrong (DOCKER_HOST socket path in the URL); ssh key/auth issues; remote Docker older than 18.09 so `docker system dial-stdio` is an unknown subcommand. The message bundles all of these and points at the version requirement.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/b9b7a4224daa9ddd. Report an issue: GitHub.

Appendix: source

Thrown at cli/connhelper/commandconn/commandconn.go:161

		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)
	// check after the call to Read, since
	// it is blocking, and while waiting on it
	// Close might get called
	if c.closing.Load() {
		// If we're currently closing the connection
		// we don't want to call onEOF
		return n, err
	}

	return n, c.handleEOF(err)

View on GitHub (pinned to 4f84911bfe)