{"record":{"id":"56dff8409186651b","repo":"juanfont/headscale","slug":"extracting-logs-w","errorCode":null,"errorMessage":"extracting logs: %w","messagePattern":"extracting logs: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"cmd/hi/docker.go","lineNumber":769,"sourceCode":"\t\t\t}\n\t\t}\n\t}\n\n\treturn testRunContainers\n}\n\n// extractContainerArtifacts saves logs and tar files from a container.\nfunc extractContainerArtifacts(ctx context.Context, cli *client.Client, containerID, containerName, logsDir string, verbose bool) error {\n\t// Ensure the logs directory exists\n\terr := os.MkdirAll(logsDir, defaultDirPerm)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"creating logs directory: %w\", err)\n\t}\n\n\t// Extract container logs\n\terr = extractContainerLogs(ctx, cli, containerID, containerName, logsDir, verbose)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"extracting logs: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// extractContainerLogs saves the stdout and stderr logs from a container to files.\nfunc extractContainerLogs(ctx context.Context, cli *client.Client, containerID, containerName, logsDir string, verbose bool) error {\n\t// Get container logs\n\tlogReader, err := cli.ContainerLogs(ctx, containerID, container.LogsOptions{\n\t\tShowStdout: true,\n\t\tShowStderr: true,\n\t\tTimestamps: false,\n\t\tFollow:     false,\n\t\tTail:       \"all\",\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"getting container logs: %w\", err)\n\t}","sourceCodeStart":751,"sourceCodeEnd":787,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/cmd/hi/docker.go#L751-L787","documentation":"Wrapped error from extractContainerLogs while saving a container's stdout/stderr logs after an integration-test run. The `hi` test runner extracts artifacts (per AGENTS.md, ~100 MB of logs per run under control_logs/{runID}/) from every hs-*/ts-* container, and this is the outer wrapper around that extraction. The underlying cause is almost always a Docker API call failure (container already removed, daemon unreachable, cancelled context), not the wrapper itself.","triggerScenarios":"Calling extractContainerArtifacts after a test run when the container was force-removed before log extraction (e.g. cleanup ran first), when the Docker daemon connection dropped, or when ctx was cancelled/timed out before cli.ContainerLogs could complete.","commonSituations":"Stale containers from a previous run that `hi cleanup` already reaped; Docker Desktop restarting mid-run; the run timeout firing and cancelling the context during artifact extraction; disk pressure in the Docker VM.","solutions":["Run `docker ps -a --filter name=hs-` to check whether the container still exists; re-run with `go run ./cmd/hi cleanup` first to clear stale state","Verify the Docker daemon is healthy with `docker info` and restart it if the connection dropped","Increase the run timeout (runConfig.Timeout) so extraction is not racing a cancelled context","Read the wrapped error text — it names the exact failing stage (getting logs, demultiplexing, or writing files) which is the real problem"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Before extraction, confirm the container still exists\nif _, err := cli.ContainerInspect(ctx, containerID); err != nil {\n    // skip artifact extraction instead of failing the whole cleanup\n    log.Printf(\"container %s gone, skipping log extraction\", containerName)\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run `go run ./cmd/hi cleanup` before starting a new test run to avoid stale/removed containers","Treat artifact-extraction failures as non-fatal: the test verdict lives in the test output, not the logs","Keep disk headroom — each run writes ~100 MB to control_logs/"],"tags":["docker","logging","integration-tests","hi-runner"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}