juanfont/headscale · error

cleaning stale test containers: %w

Error message

cleaning stale test containers: %w

What it means

Returned by cleanupBeforeTest in the hi integration-test runner when cleanupStaleTestContainers fails. That helper creates a Docker client and lists containers filtered to status exited/dead, so failures are almost always Docker daemon connectivity, permissions, or a malformed filter. The error wraps the underlying cause with %w, so the text after the colon identifies the real problem.

Source

Thrown at cmd/hi/cleanup.go:25

	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/cenkalti/backoff/v5"
	"github.com/docker/docker/api/types/container"
	"github.com/docker/docker/api/types/filters"
	"github.com/docker/docker/api/types/image"
	"github.com/docker/docker/client"
	"github.com/docker/docker/errdefs"
)

// cleanupBeforeTest performs cleanup operations before running tests.
// Only removes stale (stopped/exited) test containers to avoid interfering with concurrent test runs.
func cleanupBeforeTest(ctx context.Context) error {
	err := cleanupStaleTestContainers(ctx)
	if err != nil {
		return fmt.Errorf("cleaning stale test containers: %w", err)
	}

	if err := pruneDockerNetworks(ctx); err != nil { //nolint:noinlineerr
		return fmt.Errorf("pruning networks: %w", err)
	}

	return nil
}

// cleanupAfterTest removes the test container and all associated integration test containers for the run.
func cleanupAfterTest(ctx context.Context, cli *client.Client, containerID, runID string) error {
	// Remove the main test container
	err := cli.ContainerRemove(ctx, containerID, container.RemoveOptions{
		Force: true,
	})
	if err != nil {
		return fmt.Errorf("removing test container: %w", err)
	}

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Start Docker and verify with `docker ps` — the same permission context is what the test runner uses
  2. Check DOCKER_HOST and DOCKER_CONTEXT environment variables; unset them or point them at a live daemon
  3. Add your user to the docker group (sudo usermod -aG docker $USER) and re-login, or run with adequate privileges
  4. Read the wrapped error text after 'cleaning stale test containers:' — it names the exact Docker API failure
  5. If the environment has no usable Docker, run `hi doctor` to diagnose the setup

Example fix

# before: daemon not running
go run ./cmd/hi run TestACL
# error: cleaning stale test containers: ... connect /var/run/docker.sock

# after
systemctl start docker   # or start Docker Desktop
go run ./cmd/hi run TestACL
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight: confirm a Docker daemon is reachable before running hi.
func dockerReachable() bool {
	cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
	if err != nil {
		return false
	}
	defer cli.Close()
	_, err = cli.Info(context.Background())
	return err == nil
}

Try / catch

Wrap the hi invocation; on failure inspect errors.Unwrap — daemon connectivity errors are retryable after starting Docker, permission errors are not.

Prevention

When it happens

Trigger: Running `hi run` (or any command that triggers pre-test cleanup) when the Docker daemon is stopped, DOCKER_HOST points to an unreachable socket, the user lacks permission on /var/run/docker.sock, or the Docker API version on the host rejects the status filter combination.

Common situations: Docker Desktop not started; running as a non-root user outside the docker group; DOCKER_HOST/DOCKER_CONTEXT env vars pointing at a stale remote daemon; rootless Docker not running; older Docker engines that do not support the 'dead' status filter.

Related errors


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