{"record":{"id":"ff1abed889878110","repo":"sipeed/picoclaw","slug":"failed-to-open-log-file-w","errorCode":null,"errorMessage":"failed to open log file: %w","messagePattern":"failed to open log file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/logger/logger.go","lineNumber":186,"sourceCode":"\tif s == \"\" {\n\t\treturn\n\t}\n\tif level, ok := ParseLevel(s); ok {\n\t\tSetLevel(level)\n\t}\n}\n\nfunc EnableFileLogging(filePath string) error {\n\tmu.Lock()\n\tdefer mu.Unlock()\n\n\tif err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil {\n\t\treturn fmt.Errorf(\"failed to create log directory: %w\", err)\n\t}\n\n\tnewFile, err := os.OpenFile(filePath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open log file: %w\", err)\n\t}\n\n\t// Close old file if exists\n\tif logFile != nil {\n\t\tlogFile.Close()\n\t}\n\n\tlogFile = newFile\n\n\tif len(writers) != 1 {\n\t\treturn fmt.Errorf(\"failed to configure file logging: %w\", err)\n\t}\n\n\twriters = append(writers, logFile)\n\tlogger = logger.Output(io.MultiWriter(writers...))\n\n\treturn nil\n}","sourceCodeStart":168,"sourceCodeEnd":204,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/logger/logger.go#L168-L204","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\n$ sudo picoclaw ...   # creates /var/log/picoclaw/app.log as root\n$ picoclaw ...         # fails: permission denied\n\n# after\n$ sudo rm /var/log/picoclaw/app.log   # or: chown $(id -u) /var/log/picoclaw/app.log","handlingStrategy":"try-catch","validationCode":"// verify the exact file can be opened for append before wiring the logger\nf, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)\nif err != nil {\n    return fmt.Errorf(\"log path not usable (%w); fix ownership/mount or use stdout logging\", err)\n}\nf.Close()","typeGuard":null,"tryCatchPattern":"if err := logger.EnableFileLogging(path); err != nil {\n    var errno syscall.Errno\n    if errors.As(err, &errno) && errno == syscall.EACCES {\n        // stale root-owned log from a previous privileged run\n        return fmt.Errorf(\"log file %s not writable by uid %d — remove or chown it\", path, os.Getuid())\n    }\n    fmt.Fprintf(os.Stderr, \"file logging unavailable: %v\\n\", err)\n}","preventionTips":["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"],"tags":["logging","filesystem","permissions"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}