juanfont/headscale · error

starting container: %w

Error message

starting container: %w

What it means

Returned when `cli.ContainerStart` fails for the freshly created test container. Creation succeeded but the daemon refused to start — most often due to port conflicts on published ports, invalid bind mounts, or runtime constraints. The container remains created (not running), so cleanup is still needed.

Source

Thrown at cmd/hi/docker.go:96

		log.Printf("Command: %s", strings.Join(goTestCmd, " "))
	}

	imageName := "golang:" + config.GoVersion
	if err := ensureImageAvailable(ctx, cli, imageName, config.Verbose); err != nil { //nolint:noinlineerr
		return fmt.Errorf("ensuring image availability: %w", err)
	}

	resp, err := createGoTestContainer(ctx, cli, config, containerName, absLogsDir, goTestCmd)
	if err != nil {
		return fmt.Errorf("creating container: %w", err)
	}

	if config.Verbose {
		log.Printf("Created container: %s", resp.ID)
	}

	if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil { //nolint:noinlineerr
		return fmt.Errorf("starting container: %w", err)
	}

	log.Printf("Starting test: %s", config.TestPattern)
	log.Printf("Run ID: %s", runID)
	log.Printf("Monitor with: docker logs -f %s", containerName)
	log.Printf("Logs directory: %s", logsDir)

	// Start stats collection for container resource monitoring (if enabled)
	var statsCollector *StatsCollector

	if config.Stats {
		var err error

		statsCollector, err = NewStatsCollector(ctx)
		if err != nil {
			if config.Verbose {
				log.Printf("Warning: failed to create stats collector: %v", err)
			}

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Find and stop the port/container conflict: `docker ps` then `docker rm -f` the stale containers, or `go run ./cmd/hi clean`.
  2. Avoid running two `hi run` commands concurrently against the same Docker daemon.
  3. Read the wrapped daemon message — it names the conflicting port or mount.
  4. Enable swap-accounting cgroup flags if memory limits are set (kernel cmdline `swapaccount=1`).
Defensive patterns

Strategy: validation

Validate before calling

// free the ports the test will publish before starting
for _, port := range []string{"8080/tcp", "3478/udp"} { // adjust to actual published set
    out, _ := exec.Command("sh", "-c", fmt.Sprintf(
        "docker ps -q --filter publish=%s", port)).Output()
    if len(strings.TrimSpace(string(out))) > 0 {
        return fmt.Errorf("port %s already bound by another container", port)
    }
}

Try / catch

if err := runDockerTest(ctx, config); err != nil {
    if strings.Contains(err.Error(), "starting container") {
        // inspect `docker ps -a` for the created-but-not-started container,
        // resolve the port/mount conflict named in the wrapped daemon error, then retry
    }
}

Prevention

When it happens

Trigger: Another `hi` run (or any process) already binds the headscale/Tailscale ports this container publishes; the bind-mounted logs dir disappeared between create and start; the daemon cannot apply the host config (memory limits without swap accounting: 'WARNING: Your kernel does not support swap limit capabilities').

Common situations: Two concurrent `hi run` invocations racing for the same ports; leftover tailscale/headscale containers from a previous run; Linux hosts missing cgroup swap accounting when memory limits are configured.

Related errors


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