juanfont/headscale · error
writing stdout log: %w
Error message
writing stdout log: %w
What it means
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.
Source
Thrown at cmd/hi/docker.go:805
}
defer logReader.Close()
// Create log files following the headscale naming convention
stdoutPath := filepath.Join(logsDir, containerName+".stdout.log")
stderrPath := filepath.Join(logsDir, containerName+".stderr.log")
// Create buffers to capture stdout and stderr separately
var stdoutBuf, stderrBuf bytes.Buffer
// Demultiplex the Docker logs stream to separate stdout and stderr
_, err = stdcopy.StdCopy(&stdoutBuf, &stderrBuf, logReader)
if err != nil {
return fmt.Errorf("demultiplexing container logs: %w", err)
}
// Write stdout logs
if err := os.WriteFile(stdoutPath, stdoutBuf.Bytes(), 0o644); err != nil { //nolint:gosec,noinlineerr // log files should be readable
return fmt.Errorf("writing stdout log: %w", err)
}
// Write stderr logs
if err := os.WriteFile(stderrPath, stderrBuf.Bytes(), 0o644); err != nil { //nolint:gosec,noinlineerr // log files should be readable
return fmt.Errorf("writing stderr log: %w", err)
}
if verbose {
log.Printf("Saved logs for %s: %s, %s", containerName, stdoutPath, stderrPath)
}
return nil
}
View on GitHub (pinned to 565fd254d0)
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
Defensive patterns
Strategy: validation
Validate before calling
// Verify the target is writable before the run starts
if err := os.MkdirAll(logsDir, 0o755); err != nil { return err }
probe := filepath.Join(logsDir, ".writeprobe")
if err := os.WriteFile(probe, nil, 0o644); err != nil { return err }
os.Remove(probe) Try / catch
if err := os.WriteFile(stdoutPath, stdoutBuf.Bytes(), 0o644); err != nil {
if errors.Is(err, syscall.ENOSPC) {
// disk full: fall back to stderr-only or a summary so the run still reports
}
return fmt.Errorf("writing stdout log: %w", err)
} Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- writing stderr log: %w
- getting absolute path for logs directory: %w
- creating logs directory: %w
- extracting logs: %w
- reading map responses from directory: %w
AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15).
Data as JSON: /api/errors/f6dfbe080d3b8a7d.
Report an issue: GitHub.