{"record":{"id":"49111e2cb0618681","repo":"sipeed/picoclaw","slug":"failed-to-create-log-directory-w","errorCode":null,"errorMessage":"failed to create log directory: %w","messagePattern":"failed to create log directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/logger/logger.go","lineNumber":181,"sourceCode":"}\n\n// SetLevelFromString sets the log level from a string value.\n// If the string is empty or not a recognized level name, the current level is kept.\nfunc SetLevelFromString(s string) {\n\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","sourceCodeStart":163,"sourceCodeEnd":199,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/logger/logger.go#L163-L199","documentation":"EnableFileLogging first ensures the parent directory of the requested log file exists via os.MkdirAll(filepath.Dir(filePath), 0o755). If that mkdir fails, the wrapped OS error is returned and file logging is never enabled (the package keeps its previous sink; the mutex-guarded state is unchanged).","triggerScenarios":"(1) EACCES: log directory path under a root-owned or non-writable parent (e.g. /var/log without privileges); (2) ENOTDIR: a path component is a regular file (\"/tmp/x\" is a file, log path \"/tmp/x/app.log\"); (3) EROFS: read-only filesystem; (4) relative filePath whose parent resolves against an unexpected cwd; (5) ENAMETOOLONG.","commonSituations":"Pointing log_file at /var/log/... while running unprivileged; running under systemd without WriteDirectories/permissions; a stale file squatting on a directory name; read-only container mounts for config-derived log paths.","solutions":["Choose a log path under a directory the process can write to (e.g. within the instance/data dir)","Pre-create the directory with correct ownership (install -d -o app /var/log/app)","Remove any regular file occupying a needed directory component","Use an absolute filePath so the parent dir does not depend on the working directory"],"exampleFix":"# before\nEnableFileLogging(\"/var/log/picoclaw/app.log\")  # unprivileged user\n\n# after\nEnableFileLogging(\"/home/app/.picoclaw/logs/app.log\")","handlingStrategy":"validation","validationCode":"func logDirReady(path string) bool {\n    dir := filepath.Dir(path)\n    if err := os.MkdirAll(dir, 0o755); err != nil {\n        return false\n    }\n    probe := filepath.Join(dir, \".probe\")\n    if err := os.WriteFile(probe, nil, 0o644); err != nil {\n        return false\n    }\n    _ = os.Remove(probe)\n    return true\n}","typeGuard":null,"tryCatchPattern":"if err := logger.EnableFileLogging(path); err != nil {\n    // keep the process alive on stdout/stderr logging, but surface the misconfiguration\n    fmt.Fprintf(os.Stderr, \"file logging unavailable (%v); continuing on stderr\\n\", err)\n}","preventionTips":["Default log paths into an app-owned directory instead of /var/log","Create log directories in the install/package step with correct ownership","Always pass absolute log paths so behavior does not depend on cwd"],"tags":["logging","filesystem","permissions"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}