{"record":{"id":"0a399d4eec5d5800","repo":"juanfont/headscale","slug":"waiting-for-container-w","errorCode":null,"errorMessage":"waiting for container: %w","messagePattern":"waiting for container: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/hi/docker.go","lineNumber":341,"sourceCode":"\tout, err := cli.ContainerLogs(ctx, containerID, container.LogsOptions{\n\t\tShowStdout: true,\n\t\tShowStderr: true,\n\t\tFollow:     true,\n\t})\n\tif err != nil {\n\t\treturn -1, fmt.Errorf(\"getting container logs: %w\", err)\n\t}\n\tdefer out.Close()\n\n\tgo func() {\n\t\t_, _ = io.Copy(os.Stdout, out)\n\t}()\n\n\tstatusCh, errCh := cli.ContainerWait(ctx, containerID, container.WaitConditionNotRunning)\n\tselect {\n\tcase err := <-errCh:\n\t\tif err != nil {\n\t\t\treturn -1, fmt.Errorf(\"waiting for container: %w\", err)\n\t\t}\n\tcase status := <-statusCh:\n\t\treturn int(status.StatusCode), nil\n\t}\n\n\treturn -1, ErrUnexpectedContainerWait\n}\n\n// waitForContainerFinalization ensures all test containers have properly finished and flushed their output.\nfunc waitForContainerFinalization(ctx context.Context, cli *client.Client, testContainerID string, verbose bool) error {\n\t// First, get all related test containers\n\tcontainers, err := cli.ContainerList(ctx, container.ListOptions{All: true})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"listing containers: %w\", err)\n\t}\n\n\ttestContainers := getCurrentTestContainers(containers, testContainerID, verbose)\n","sourceCodeStart":323,"sourceCodeEnd":359,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/cmd/hi/docker.go#L323-L359","documentation":"Returned by `streamAndWait` when the `cli.ContainerWait` error channel yields an error — the container stopped in a way that produced no clean status, or the wait call itself failed (context cancelled, daemon error). Distinct from `ErrUnexpectedContainerWait`, which fires only when the select falls through with neither channel delivering.","triggerScenarios":"Context passed to runDockerTest is cancelled (Ctrl-C propagating, parent timeout); daemon error while waiting (container forcibly removed with `docker rm -f`); wait API interrupted by daemon restart.","commonSituations":"User interrupts the run; CI job timeout cancels the command; another terminal runs `hi cleanup` or `docker prune` mid-test.","solutions":["If interrupted intentionally, clean up with `go run ./cmd/hi clean` before the next run.","Raise the CI/job timeout if the suite legitimately needs longer.","Verify no concurrent prune/cleanup runs during tests.","Re-run; artifacts under control_logs/ survive for diagnosis."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := runDockerTest(ctx, config); err != nil {\n    if errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {\n        // caller cancelled: run `go run ./cmd/hi clean` before the next attempt\n    } else if strings.Contains(err.Error(), \"waiting for container\") {\n        // daemon-side wait failure: check daemon health, then retry\n    }\n}","preventionTips":["Size outer context timeouts to config.Timeout plus overhead.","Prevent concurrent cleanup/prune jobs from removing containers mid-wait.","Handle SIGINT by cleaning up (hi clean) before restarting."],"tags":["docker","container-wait","cancellation","hi-runner"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}