{"record":{"id":"f6dfbe080d3b8a7d","repo":"juanfont/headscale","slug":"writing-stdout-log-w","errorCode":null,"errorMessage":"writing stdout log: %w","messagePattern":"writing stdout log: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/hi/docker.go","lineNumber":805,"sourceCode":"\t}\n\tdefer logReader.Close()\n\n\t// Create log files following the headscale naming convention\n\tstdoutPath := filepath.Join(logsDir, containerName+\".stdout.log\")\n\tstderrPath := filepath.Join(logsDir, containerName+\".stderr.log\")\n\n\t// Create buffers to capture stdout and stderr separately\n\tvar stdoutBuf, stderrBuf bytes.Buffer\n\n\t// Demultiplex the Docker logs stream to separate stdout and stderr\n\t_, err = stdcopy.StdCopy(&stdoutBuf, &stderrBuf, logReader)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"demultiplexing container logs: %w\", err)\n\t}\n\n\t// Write stdout logs\n\tif err := os.WriteFile(stdoutPath, stdoutBuf.Bytes(), 0o644); err != nil { //nolint:gosec,noinlineerr // log files should be readable\n\t\treturn fmt.Errorf(\"writing stdout log: %w\", err)\n\t}\n\n\t// Write stderr logs\n\tif err := os.WriteFile(stderrPath, stderrBuf.Bytes(), 0o644); err != nil { //nolint:gosec,noinlineerr // log files should be readable\n\t\treturn fmt.Errorf(\"writing stderr log: %w\", err)\n\t}\n\n\tif verbose {\n\t\tlog.Printf(\"Saved logs for %s: %s, %s\", containerName, stdoutPath, stderrPath)\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":787,"sourceCodeEnd":819,"githubUrl":"https://github.com/juanfont/headscale/blob/565fd254d06c4c7f9a8cad1714a43445c79ba420/cmd/hi/docker.go#L787-L819","documentation":"os.WriteFile failed while writing the demultiplexed stdout log to <logsDir>/<containerName>.stdout.log. This is a plain filesystem error: the logs directory does not exist at write time, permissions on the directory are wrong, the path is not writable, or the disk is full. Integration runs write ~100 MB of logs, so ENOSPC is a common cause.","triggerScenarios":"os.WriteFile(stdoutPath, ...) failing with ENOENT (logsDir removed between MkdirAll and write), EACCES/EPERM (directory owned by another user), or ENOSPC (disk full after ~100 MB of logs from prior runs).","commonSituations":"control_logs/ filling the disk or a tmpfs-backed workspace running out of space; running `hi` under a different user than the one that created control_logs/ (permission mismatch); the workspace directory being cleaned concurrently.","solutions":["Check `df -h .` and prune old runs under control_logs/ — runs generate ~100 MB of logs each","Verify write permission on the target directory: `ls -ld control_logs/<runID>` and fix ownership if it differs from the current user","Ensure nothing deletes logsDir between test start and artifact extraction (concurrent cleanup job)","Re-run the test after freeing space"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Verify the target is writable before the run starts\nif err := os.MkdirAll(logsDir, 0o755); err != nil { return err }\nprobe := filepath.Join(logsDir, \".writeprobe\")\nif err := os.WriteFile(probe, nil, 0o644); err != nil { return err }\nos.Remove(probe)","typeGuard":null,"tryCatchPattern":"if err := os.WriteFile(stdoutPath, stdoutBuf.Bytes(), 0o644); err != nil {\n    if errors.Is(err, syscall.ENOSPC) {\n        // disk full: fall back to stderr-only or a summary so the run still reports\n    }\n    return fmt.Errorf(\"writing stdout log: %w\", err)\n}","preventionTips":["Pre-flight check free disk space before each run (runs need ~100 MB)","Run `hi` as the user that owns control_logs/","Archive/prune old control_logs/{runID}/ directories in CI between jobs"],"tags":["filesystem","disk-full","logging","integration-tests"],"backgroundTag":null,"analyzedSha":"565fd254d06c4c7f9a8cad1714a43445c79ba420","analyzedAt":"2026-08-15T13:12:30.133Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}