docker/cli · error
ssh host connection is not valid
Error message
ssh host connection is not valid: %w
What it means
Top-level wrapper produced by getConnectionHelper (connhelper.go:47-50) when the DOCKER_HOST URL scheme is `ssh` but ssh.NewSpec fails to build a valid Spec from the parsed URL. The inner error details the precise reason (wrong scheme, empty host, query/fragment present, password in URL, etc.).
Solutions
- Read the wrapped inner error to identify the exact disallowed element.
- Strip query parameters and fragments from the ssh URL; DOCKER_HOST ssh URLs only support user@host[:port][/socket].
- Never embed a password in the URL — use an ssh key or agent instead.
- Ensure the hostname is present and the scheme is exactly `ssh`.
Example fix
# before export DOCKER_HOST='ssh://user:pass@host:22?strict=no#frag' # after — bare user@host[:port][/socket] export DOCKER_HOST='ssh://user@host:22/var/run/docker.sock'
Defensive patterns
Strategy: validation
Validate before calling
// Validate the ssh URL shape before passing to GetConnectionHelper.
u, err := url.Parse(daemonURL)
if err != nil || u.Scheme != "ssh" || u.Hostname() == "" || u.RawQuery != "" || u.Fragment != "" {
return errors.New("invalid DOCKER_HOST ssh URL")
} Try / catch
helper, err := connhelper.GetConnectionHelper(daemonURL)
if err != nil {
return fmt.Errorf("invalid docker host URL: %w", err)
} Prevention
- Keep DOCKER_HOST ssh URLs to the form `ssh://[user@]host[:port][/socket]`.
- Never embed passwords, query params, or fragments in the URL.
- Set ssh options in ~/.ssh/config instead.
When it happens
Trigger: GetConnectionHelper / GetConnectionHelperWithSSHOpts is called with a daemonURL whose scheme is `ssh`; url.Parse succeeds but ssh.NewSpec(u) returns an error (delegating to newSpec validation). The error is wrapped as `ssh host connection is not valid: <inner>`.
Common situations: DOCKER_HOST is set to an ssh URL with a disallowed component: a query string (`?x=1`), a fragment (`#top`), an embedded password (`ssh://user:pass@host`), or an empty hostname. Also when the scheme is missing or misspelled so url.Parse misinterprets the host.
Related errors
- no scheme provided
- incorrect scheme:
- plain-text password is not supported
- hostname is empty
- no remote command specified
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/3ab6be90ea47e208.
Report an issue: GitHub.
Appendix: source
Thrown at cli/connhelper/connhelper.go:50
// GetConnectionHelperWithSSHOpts returns Docker-specific connection helper for
// the given URL, and accepts additional options for ssh connections. It returns
// nil without error when no helper is registered for the scheme.
//
// Requires Docker 18.09 or later on the remote host.
func GetConnectionHelperWithSSHOpts(daemonURL string, sshFlags []string) (*ConnectionHelper, error) {
return getConnectionHelper(daemonURL, sshFlags)
}
func getConnectionHelper(daemonURL string, sshFlags []string) (*ConnectionHelper, error) {
u, err := url.Parse(daemonURL)
if err != nil {
return nil, err
}
if u.Scheme == "ssh" {
sp, err := ssh.NewSpec(u)
if err != nil {
return nil, fmt.Errorf("ssh host connection is not valid: %w", err)
}
sshFlags = addSSHTimeout(sshFlags)
sshFlags = disablePseudoTerminalAllocation(sshFlags)
remoteCommand := []string{"docker", "system", "dial-stdio"}
socketPath := sp.Path
if strings.Trim(sp.Path, "/") != "" {
remoteCommand = []string{"docker", "--host=unix://" + socketPath, "system", "dial-stdio"}
}
sshArgs, err := sp.Command(sshFlags, remoteCommand...)
if err != nil {
return nil, err
}
return &ConnectionHelper{
Dialer: func(ctx context.Context, network, addr string) (net.Conn, error) {
return commandconn.New(ctx, "ssh", sshArgs...)
},
Host: "http://docker.example.com",View on GitHub (pinned to 4f84911bfe)