docker/cli · error

plain-text password is not supported

Error message

plain-text password is not supported

What it means

Returned by newSpec when the URL's userinfo contains a password (u.User.Password() reports one is present). Embedding a plaintext password in DOCKER_HOST would expose it in process listings and shell history, so the SSH helper refuses it and expects key-based or agent-based authentication instead.

Solutions

  1. Remove the password from the URL: 'ssh://user@host'.
  2. Use SSH key authentication (configure an SSH key or ssh-agent) instead of a password.
  3. Store credentials in ~/.ssh/config or use an ssh-agent rather than the URL.

Example fix

# before
DOCKER_HOST=ssh://user:s3cret@remote-host
# after
DOCKER_HOST=ssh://user@remote-host
# (then authenticate via SSH key / agent)
Defensive patterns

Strategy: validation

Validate before calling

// Strip embedded passwords from DOCKER_HOST before use.
u, err := url.Parse(host)
if err == nil && u.User != nil {
    if _, ok := u.User.Password(); ok {
        u.User = url.User(u.User.Username()) // drop password
    }
    host = u.String()
}

Type guard

func hasNoPassword(s string) bool {
    u, err := url.Parse(s)
    return err == nil && (u.User == nil || func() bool { _, ok := u.User.Password(); return !ok }())
}

Prevention

When it happens

Trigger: Setting DOCKER_HOST='ssh://user:password@host' or passing such a URL to ssh.ParseURL/NewSpec. Any URL of the form scheme://user:pass@host triggers it.

Common situations: A user tries to embed SSH credentials directly in the DOCKER_HOST value for convenience in CI. Copying a URL with embedded credentials from a secrets manager.

Related errors


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

Appendix: source

Thrown at cli/connhelper/ssh/ssh.go:54

}

func newSpec(u *url.URL) (*Spec, error) {
	if u == nil {
		return nil, errors.New("URL is nil")
	}
	if u.Scheme == "" {
		return nil, errors.New("no scheme provided")
	}
	if u.Scheme != "ssh" {
		return nil, errors.New("incorrect scheme: " + u.Scheme)
	}

	var sp Spec

	if u.User != nil {
		sp.User = u.User.Username()
		if _, ok := u.User.Password(); ok {
			return nil, errors.New("plain-text password is not supported")
		}
	}
	sp.Host = u.Hostname()
	if sp.Host == "" {
		return nil, errors.New("hostname is empty")
	}
	sp.Port = u.Port()
	sp.Path = u.Path
	if u.RawQuery != "" {
		return nil, fmt.Errorf("query parameters are not allowed: %q", u.RawQuery)
	}
	if u.Fragment != "" {
		return nil, fmt.Errorf("fragments are not allowed: %q", u.Fragment)
	}

	return &sp, nil
}

View on GitHub (pinned to 4f84911bfe)