docker/cli · error
invalid proto, expected tcp
Error message
invalid proto, expected tcp: %s
What it means
ParseTCPAddr rejects a tcp address that, after stripping the tcp:// prefix, still contains '://' (a nested scheme) or is empty in a way that is not the special-case bare 'tcp://'. It enforces that exactly one tcp scheme is present and that there is a usable remainder.
Solutions
- Use a single tcp scheme: tcp://host:port.
- Remove any nested '://' from the address.
- For the default port, use tcp://host or tcp://:2375 rather than nesting.
- Validate the address contains exactly one '://' before ParseTCPAddr.
Example fix
# before docker -H tcp://unix:///var/run/docker.sock ps # after docker -H tcp://127.0.0.1:2375 ps
Defensive patterns
Strategy: validation
Validate before calling
// Ensure a tcp address has exactly one tcp scheme and a usable remainder.
func validTCPHost(s string) error {
if strings.Count(s, "://") != 1 || !strings.HasPrefix(s, "tcp://") {
return fmt.Errorf("expected a single tcp:// scheme in %q", s)
}
if strings.TrimPrefix(s, "tcp://") == "" {
return fmt.Errorf("empty tcp address in %q", s)
}
return nil
} Prevention
- Use exactly one tcp:// scheme per -H value.
- Do not nest schemes; switch protocols with separate -H flags.
- Validate the address shape before passing to ParseTCPAddr.
When it happens
Trigger: -H tcp:// with a nested scheme (tcp://unix://x) or a value that reduces to empty after the prefix in an unexpected form, e.g. tcp:// followed by another scheme token.
Common situations: Mixing protocols in one -H value; malformed -H from templating; accidentally prefixing an already-prefixed address.
Related errors
- invalid bind address format
- invalid proto, expected
- invalid spec
- invalid count ( ): value must be either "all" or an integer
- gpu request key ' ' can be specified only once
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/81a5bf1163fad978.
Report an issue: GitHub.
Appendix: source
Thrown at opts/hosts.go:105
}
if addr == "" {
addr = defaultAddr
}
return fmt.Sprintf("%s://%s", proto, addr), nil
}
// ParseTCPAddr parses and validates that the specified address is a valid TCP
// address. It returns a formatted TCP address, either using the address parsed
// from tryAddr, or the contents of defaultAddr if tryAddr is a blank string.
// tryAddr is expected to have already been Trim()'d
// defaultAddr must be in the full `tcp://host:port` form
func ParseTCPAddr(tryAddr string, defaultAddr string) (string, error) {
if tryAddr == "" || tryAddr == "tcp://" {
return defaultAddr, nil
}
addr := strings.TrimPrefix(tryAddr, "tcp://")
if strings.Contains(addr, "://") || addr == "" {
return "", fmt.Errorf("invalid proto, expected tcp: %s", tryAddr)
}
defaultAddr = strings.TrimPrefix(defaultAddr, "tcp://")
defaultHost, defaultPort, err := net.SplitHostPort(defaultAddr)
if err != nil {
return "", err
}
// url.Parse fails for trailing colon on IPv6 brackets on Go 1.5, but
// not 1.4. See https://github.com/golang/go/issues/12200 and
// https://github.com/golang/go/issues/6530.
if strings.HasSuffix(addr, "]:") {
addr += defaultPort
}
u, err := url.Parse("tcp://" + addr)
if err != nil {
return "", err
}View on GitHub (pinned to 4f84911bfe)