{"record":{"id":"264cac2d8a2b3f6a","repo":"sipeed/picoclaw","slug":"failed-to-create-log-file-s","errorCode":null,"errorMessage":"failed to create log file: %s","messagePattern":"failed to create log file: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/logger/panic.go","lineNumber":20,"sourceCode":"\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime/debug\"\n\t\"time\"\n)\n\nvar panicWriter io.WriteCloser\n\nfunc InitPanic(filePath string) (func(), error) {\n\tif err := os.MkdirAll(filepath.Dir(filePath), 0o755); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create log directory: %w\", err)\n\t}\n\twriter := initPanicFile(filePath)\n\tif writer == nil {\n\t\treturn nil, fmt.Errorf(\"failed to create log file: %s\", filePath)\n\t}\n\tif panicWriter != nil {\n\t\t_ = panicWriter.Close()\n\t}\n\tpanicWriter = writer\n\treturn func() {\n\t\tdefer func() {\n\t\t\twriter.Close()\n\t\t\tpanicWriter = nil\n\t\t}()\n\t\tif err := recover(); err != nil {\n\t\t\tRecoverPanicNoExit(err)\n\n\t\t\tos.Exit(1)\n\t\t}\n\t}, nil\n}\n","sourceCodeStart":2,"sourceCodeEnd":38,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/logger/panic.go#L2-L38","documentation":"After the directory is ensured, InitPanic delegates to the platform-specific initPanicFile(filePath) (panic_unix.go / panic_win.go), which opens the panic log file and returns nil on any open failure. This error is raised when that writer comes back nil — the message prints only the path, so the actual OS error (permissions, EISDIR, EROFS, name too long) is swallowed by the platform helper and must be diagnosed externally.","triggerScenarios":"The file open inside initPanicFile failing: existing panic.log owned by another user (prior sudo run), filePath naming a directory, read-only filesystem, SELinux/AppArmor denial, ENAMETOOLONG. Because the helper discards the errno, callers only learn that creation failed for that path.","commonSituations":"Root-owned panic.log after earlier privileged runs; log location on a read-only volume; MAC (SELinux) blocking the open with no errno surfaced to the caller.","solutions":["Manually test writability of the exact path as the running user (touch <path>) to surface the real OS error","Fix ownership/permissions of the existing file or delete it so it can be recreated","Choose a writable absolute path on a writable filesystem","If diagnosing a hardened host, check audit logs (SELinux/AppArmor) for the denied open"],"exampleFix":"# before\nInitPanic(\"/var/log/picoclaw/panic.log\")  # returns nil-writer error, cause hidden\n\n# after: reproduce the hidden error, then fix ownership\n$ sudo -u appuser touch /var/log/picoclaw/panic.log\n# touch: cannot touch ...: Permission denied\n$ sudo chown appuser /var/log/picoclaw/panic.log","handlingStrategy":"validation","validationCode":"// reproduce the hidden open error before relying on panic capture\nf, err := os.OpenFile(panicPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0o644)\nif err != nil {\n    return fmt.Errorf(\"panic log %s not openable (real cause: %w)\", panicPath, err)\n}\nf.Close()","typeGuard":null,"tryCatchPattern":"if _, err := logger.InitPanic(panicPath); err != nil {\n    // message only carries the path — the errno is lost, so probe the path directly for diagnosis\n    fmt.Fprintf(os.Stderr, \"panic capture failed for %s; probe the path for the real error: %v\\n\", panicPath, err)\n}","preventionTips":["Verify the panic log path is creatable as the running user during deployment checks","Watch for root-owned panic logs after any privileged run and chown them back"],"tags":["logging","filesystem","permissions","panic"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}