sipeed/picoclaw · error
failed to open log file: %w
Error message
failed to open log file: %w
What it means
After the log directory is created, EnableFileLogging opens the file with os.OpenFile(filePath, O_CREATE|O_WRONLY|O_APPEND, 0o644); failure is wrapped here. Common errnos: EACCES (file or parent not writable — e.g. the file exists but is owned by root from a previous sudo run), EISDIR (filePath names a directory), EROFS, ENAMETOOLONG. Note: on the success path the function also contains a latent bug — the later "failed to configure file logging" check wraps the already-nil err — so that companion message can never carry a cause.
Source
Thrown at pkg/logger/logger.go:186
if s == "" {
return
}
if level, ok := ParseLevel(s); ok {
SetLevel(level)
}
}
func EnableFileLogging(filePath string) error {
mu.Lock()
defer mu.Unlock()
if err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil {
return fmt.Errorf("failed to create log directory: %w", err)
}
newFile, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
if err != nil {
return fmt.Errorf("failed to open log file: %w", err)
}
// Close old file if exists
if logFile != nil {
logFile.Close()
}
logFile = newFile
if len(writers) != 1 {
return fmt.Errorf("failed to configure file logging: %w", err)
}
writers = append(writers, logFile)
logger = logger.Output(io.MultiWriter(writers...))
return nil
}View on GitHub (pinned to 49183d7e8d)
Solutions
- Check ownership of the existing file (ls -l) and chown/delete it so the current user can append
- Confirm filePath is a file location, not a directory
- Move logging to a writable volume/directory and use an absolute path
- If the filesystem is read-only by design, configure logging to stdout instead
Example fix
# before $ sudo picoclaw ... # creates /var/log/picoclaw/app.log as root $ picoclaw ... # fails: permission denied # after $ sudo rm /var/log/picoclaw/app.log # or: chown $(id -u) /var/log/picoclaw/app.log
Defensive patterns
Strategy: try-catch
Validate before calling
// verify the exact file can be opened for append before wiring the logger
f, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)
if err != nil {
return fmt.Errorf("log path not usable (%w); fix ownership/mount or use stdout logging", err)
}
f.Close() Try / catch
if err := logger.EnableFileLogging(path); err != nil {
var errno syscall.Errno
if errors.As(err, &errno) && errno == syscall.EACCES {
// stale root-owned log from a previous privileged run
return fmt.Errorf("log file %s not writable by uid %d — remove or chown it", path, os.Getuid())
}
fmt.Fprintf(os.Stderr, "file logging unavailable: %v\n", err)
} Prevention
- Never run the app under sudo/root once file logging targets a persistent path — it leaves root-owned files
- Add a pre-start check that opens the log file for append, failing fast with the errno
- Keep stderr logging configured as the fallback channel for startup-time diagnostics
When it happens
Trigger: Re-running the app unprivileged after it once ran as root and created the log file 0644 root:root; filePath pointing at a directory; read-only mount; SELinux denial on the log location; path exceeds NAME_MAX.
Common situations: sudo-run processes leaving root-owned logs; containers with read-only root filesystems; log paths under mounted volumes with restrictive ownership; rotating tools replacing the file with a directory.
Related errors
- failed to create log directory: %w
- failed to create log directory: %w
- failed to create log file: %s
- create output dir: %w
- failed to save config: %w
AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15).
Data as JSON: /api/errors/ff1abed889878110.
Report an issue: GitHub.