juanfont/headscale · error

creating Docker client: %w

Error message

creating Docker client: %w

What it means

client.NewClientWithOpts failed while constructing the Docker SDK client in NewStatsCollector. The client builder resolves its endpoint from the active docker context's endpoint host or DOCKER_HOST/DOCKER_TLS_SERVERCERT env vars (see createDockerClient at cmd/hi/docker.go:453) and errors when the host string is malformed or an unsupported protocol scheme is given. Note the SDK client is lazy — this failure is about client construction options, not daemon reachability.

Source

Thrown at cmd/hi/stats.go:54

	CPUUsage  float64 // CPU usage percentage
	MemoryMB  float64 // Memory usage in MB
}

// StatsCollector manages collection of container statistics.
type StatsCollector struct {
	client            *client.Client
	containers        map[string]*ContainerStats
	stopChan          chan struct{}
	wg                sync.WaitGroup
	mutex             sync.RWMutex
	collectionStarted bool
}

// NewStatsCollector creates a new stats collector instance.
func NewStatsCollector(ctx context.Context) (*StatsCollector, error) {
	cli, err := createDockerClient(ctx)
	if err != nil {
		return nil, fmt.Errorf("creating Docker client: %w", err)
	}

	return &StatsCollector{
		client:     cli,
		containers: make(map[string]*ContainerStats),
		stopChan:   make(chan struct{}),
	}, nil
}

// StartCollection begins monitoring all containers and collecting stats for hs- and ts- containers with matching run ID.
func (sc *StatsCollector) StartCollection(ctx context.Context, runID string, verbose bool) error {
	sc.mutex.Lock()
	defer sc.mutex.Unlock()

	if sc.collectionStarted {
		return ErrStatsCollectionAlreadyStarted
	}

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Print and fix the environment: `echo $DOCKER_HOST` — it must be a valid scheme like unix:///var/run/docker.sock or tcp://host:2375
  2. Check `docker context ls` and `docker context use default` to discard a broken context
  3. Unset stale DOCKER_TLS_SERVERCERT/DOCKER_CERT_PATH values: `env -u DOCKER_TLS_SERVERCERT -u DOCKER_CERT_PATH go run ./cmd/hi ...`
  4. Verify plain `docker info` works with the same environment

Example fix

# before
export DOCKER_HOST=:2375  # malformed, no scheme
go run ./cmd/hi run "TestACL"

# after
export DOCKER_HOST=tcp://127.0.0.1:2375
go run ./cmd/hi run "TestACL"
Defensive patterns

Strategy: validation

Validate before calling

if host := os.Getenv("DOCKER_HOST"); host != "" {
    u, err := url.Parse(host)
    if err != nil || (u.Scheme != "unix" && u.Scheme != "tcp" && u.Scheme != "npipe") {
        return fmt.Errorf("invalid DOCKER_HOST %q", host)
    }
}
_ = exec.LookPath("docker") // sanity: CLI sees the same daemon

Try / catch

cli, err := createDockerClient(ctx)
if err != nil {
    // construction errors are config/env problems — print env and docker context, do not retry
    return nil, fmt.Errorf("creating Docker client: %w (check DOCKER_HOST=%q and `docker context ls`)", err, os.Getenv("DOCKER_HOST"))
}

Prevention

When it happens

Trigger: NewClientWithOpts rejecting an invalid host (bad DOCKER_HOST like 'tcp://:2375' or a scheme the SDK cannot handle, e.g. missing tcp://, npipe on Linux), unreadable TLS cert files, or an API version negotiation opt that fails to parse environment values.

Common situations: DOCKER_HOST set to a typo'd or Windows-style endpoint on Linux; a docker context whose endpoint metadata is malformed; leftover DOCKER_* env vars from a remote-daemon setup; running where the context config in ~/.docker/contexts is corrupted.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/2f0d5623da176fa7. Report an issue: GitHub.