{"record":{"id":"f9232ae2ed36e9e7","repo":"wavetermdev/waveterm","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/jobmanager/jobmanager_unix.go","lineNumber":45,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open /dev/null: %w\", err)\n\t}\n\terr = unix.Dup2(int(devNull.Fd()), int(os.Stdin.Fd()))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to dup2 stdin: %w\", err)\n\t}\n\tdevNull.Close()\n\n\tlogPath := wavebase.GetRemoteJobFilePath(jobId, \"log\")\n\tlogDir := filepath.Dir(logPath)\n\terr = os.MkdirAll(logDir, 0700)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create log directory: %w\", err)\n\t}\n\n\tlogFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open log file: %w\", err)\n\t}\n\terr = unix.Dup2(int(logFile.Fd()), int(os.Stdout.Fd()))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to dup2 stdout: %w\", err)\n\t}\n\terr = unix.Dup2(int(logFile.Fd()), int(os.Stderr.Fd()))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to dup2 stderr: %w\", err)\n\t}\n\n\tlog.SetOutput(logFile)\n\tlog.Printf(\"job manager daemonized, logging to %s\\n\", logPath)\n\tlog.Printf(\"job owner clientid: %s\\n\", clientId)\n\n\tsignal.Ignore(syscall.SIGHUP)\n\n\treturn nil\n}","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/wavetermdev/waveterm/blob/a4447c1563b2df285ab89e76c82f91e1a1a49c1e/pkg/jobmanager/jobmanager_unix.go#L27-L63","documentation":"daemonize opens the job log file (O_CREATE|O_WRONLY|O_APPEND, 0600) and this error wraps the os.OpenFile failure. The daemon's stdout/stderr will be redirected to this file, so the job cannot start logging until it opens successfully.","triggerScenarios":"os.OpenFile(logPath, O_CREATE|O_WRONLY|O_APPEND, 0600) fails: log directory not writable, ENOSPC, path exceeds NAME_MAX, or the path exists with ownership/permissions preventing the current uid from writing.","commonSituations":"Log directory owned by a different uid after a prior run under root/sudo; quota or disk-full on the data volume; overly long jobId inflating the file path; immutable or ACL-restricted file left from a previous session.","solutions":["Verify the parent directory exists and is writable by the current uid (it is just created by MkdirAll 0700)","Remove or chown a stale log file owned by another uid: ls -l <logPath>","Free disk space / check quota if errno is ENOSPC","Shorten the jobId/path if NAME_MAX (255 bytes per component) is exceeded"],"exampleFix":"// before\nlogFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)\nif err != nil {\n    return fmt.Errorf(\"failed to open log file: %w\", err)\n}\n// after\nlogFile, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)\nif err != nil {\n    if errors.Is(err, syscall.EACCES) {\n        os.Remove(logPath) // drop stale file owned by another uid\n        logFile, err = os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)\n    }\n    if err != nil {\n        return fmt.Errorf(\"failed to open log file %s: %w\", logPath, err)\n    }\n}","handlingStrategy":"retry","validationCode":"logPath := wavebase.GetRemoteJobFilePath(jobId, \"log\")\nif len(filepath.Base(logPath)) > 255 {\n    return fmt.Errorf(\"log file name exceeds NAME_MAX\")\n}\nif fi, err := os.Stat(filepath.Dir(logPath)); err != nil || !fi.IsDir() {\n    return fmt.Errorf(\"log directory missing/unwritable\")\n}","typeGuard":null,"tryCatchPattern":"if err := daemonize(clientId, jobId); err != nil {\n    if strings.Contains(err.Error(), \"failed to open log file\") {\n        log.Printf(\"log open error: %v\", errors.Unwrap(err))\n        // EACCES: remove/chown stale file; ENOSPC: free space; then retry daemonize\n    }\n    return err\n}","preventionTips":["Run all job processes under a consistent uid so log files stay writable","Monitor disk usage of the job log directory","Keep job ids short to avoid path-length limits","Clean up stale log files with wrong ownership/ACLs at startup"],"tags":["go","filesystem","logging","permissions","daemonize"],"backgroundTag":"log-file-open-failed","analyzedSha":"a4447c1563b2df285ab89e76c82f91e1a1a49c1e","analyzedAt":"2026-09-01T15:26:23.972Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}