docker/compose · error
unsupported protocol for address: %s
Error message
unsupported protocol for address: %s
What it means
memnet.DialEndpoint only accepts endpoints prefixed with unix:// or npipe://; anything else (tcp://, http://, a bare path, an empty string, or a typo like unix:/ or npipes://) falls through to this error before any dial is attempted. It is a strict, fail-fast contract: memnet exists to talk over local sockets, not TCP.
Source
Thrown at internal/memnet/conn.go:33
*/
package memnet
import (
"context"
"fmt"
"net"
"strings"
)
func DialEndpoint(ctx context.Context, endpoint string) (net.Conn, error) {
if addr, ok := strings.CutPrefix(endpoint, "unix://"); ok {
return Dial(ctx, "unix", addr)
}
if addr, ok := strings.CutPrefix(endpoint, "npipe://"); ok {
return Dial(ctx, "npipe", addr)
}
return nil, fmt.Errorf("unsupported protocol for address: %s", endpoint)
}
func Dial(ctx context.Context, network, addr string) (net.Conn, error) {
var d net.Dialer
switch network {
case "unix":
if err := validateSocketPath(addr); err != nil {
return nil, err
}
return d.DialContext(ctx, "unix", addr)
case "npipe":
// N.B. this will return an error on non-Windows
return dialNamedPipe(ctx, addr)
default:
return nil, fmt.Errorf("unsupported network: %s", network)
}
}
View on GitHub (pinned to ddc4b044b6)
Solutions
- Prefix the value correctly: unix:///var/run/docker.sock (scheme + absolute path) or npipe:////./pipe/docker_engine on Windows.
- Trim whitespace and validate the endpoint string before calling DialEndpoint.
- If you need TCP, use net.Dialer.DialContext("tcp", addr) instead of memnet.DialEndpoint — memnet deliberately rejects it.
- Normalize at the config boundary so only validated endpoints reach the dialer.
Example fix
// before
conn, err := memnet.DialEndpoint(ctx, os.Getenv("DOCKER_HOST")) // may be "tcp://..." or bare path
// after
ep := strings.TrimSpace(endpoint)
if !strings.HasPrefix(ep, "unix://") && !strings.HasPrefix(ep, "npipe://") {
ep = "unix://" + ep // bare socket path
}
conn, err := memnet.DialEndpoint(ctx, ep) Defensive patterns
Strategy: validation
Validate before calling
func validMemnetEndpoint(ep string) bool {
return strings.HasPrefix(ep, "unix://") || strings.HasPrefix(ep, "npipe://")
}
if !validMemnetEndpoint(endpoint) {
return fmt.Errorf("endpoint %q must be unix:// or npipe://", endpoint)
} Type guard
func isUnixEndpoint(ep string) bool { return strings.HasPrefix(ep, "unix://") }
func isNpipeEndpoint(ep string) bool { return strings.HasPrefix(ep, "npipe://") } Try / catch
conn, err := memnet.DialEndpoint(ctx, ep)
if err != nil {
return fmt.Errorf("dial %s: %w", ep, err) // surface which endpoint was rejected
} Prevention
- Normalize endpoints to unix:///abs/path or npipe:////./pipe/name at the config boundary.
- Trim whitespace and lowercase-check scheme prefixes (CutPrefix is case-sensitive).
- Route TCP endpoints to net.Dial, never to memnet.
When it happens
Trigger: Calling memnet.DialEndpoint(ctx, endpoint) with an endpoint string that lacks an exact unix:// or npipe:// prefix — e.g. "/var/run/docker.sock", "tcp://127.0.0.1:2375", "unix:///var/run/x.sock" is fine but "UNIX://..." (case) is not (CutPrefix is case-sensitive).
Common situations: Passing DOCKER_HOST-style values straight through without normalizing (docker uses unix:///path with three slashes and also bare paths); config files storing "tcp://" endpoints fed to a socket-only dialer; empty endpoint after failed config parsing; trailing whitespace breaking the prefix match.
Related errors
- unsupported network: %s
- named pipes are only available on Windows
- socket address is too long: %s
- invalid digest %s: %w
- invalid PID (%d): only positive PIDs are allowed
AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15).
Data as JSON: /api/errors/7befb9221fb5d45f.
Report an issue: GitHub.