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
- Remove the password from the URL: 'ssh://user@host'.
- Use SSH key authentication (configure an SSH key or ssh-agent) instead of a password.
- 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
- Never embed passwords in DOCKER_HOST.
- Prefer SSH keys or an ssh-agent.
- Configure hosts in ~/.ssh/config instead of the URL.
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
- no scheme provided
- incorrect scheme:
- hostname is empty
- ssh host connection is not valid
- no remote command specified
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)