{"record":{"id":"f119eb371cba65c8","repo":"chenhg5/cc-connect","slug":"logrotate-reopen-after-rotation-failed-for-s","errorCode":null,"errorMessage":"logrotate: reopen after rotation failed for %s","messagePattern":"logrotate: reopen after rotation failed for (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"daemon/logrotate.go","lineNumber":96,"sourceCode":"\t\tw.rotateLocked()\n\t}\n\treturn n, err\n}\n\n// Rotate forces a rotation regardless of the current size. Useful for\n// tests and for SIGHUP-style \"start a new log file\" hooks. Errors are\n// logged via slog but never returned, because Write cannot surface\n// rotation errors to its caller and the alternative (dropping log\n// data) is worse than a missed rotation.\nfunc (w *RotatingWriter) Rotate() error {\n\tw.mu.Lock()\n\tdefer w.mu.Unlock()\n\tif w.file == nil {\n\t\treturn os.ErrClosed\n\t}\n\tw.rotateLocked()\n\tif w.file == nil {\n\t\treturn fmt.Errorf(\"logrotate: reopen after rotation failed for %s\", w.path)\n\t}\n\treturn nil\n}\n\n// backupPath returns the rotated-file name for the i-th backup.\n// .1 is the most recent, .N is the oldest.\nfunc (w *RotatingWriter) backupPath(i int) string {\n\treturn fmt.Sprintf(\"%s.%d\", w.path, i)\n}\n\n// rotateLocked performs the chain rotation: delete the oldest (.N),\n// shift .(N-1) -> .N, ... .1 -> .2, rename active -> .1, reopen.\n//\n// Caller must hold w.mu.\nfunc (w *RotatingWriter) rotateLocked() {\n\tw.file.Close()\n\n\t// 1. Delete the oldest, if it exists. If this fails it is not fatal —","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/chenhg5/cc-connect/blob/4000b2338aa6e850c99df54f8b0ed6ed7460b401/daemon/logrotate.go#L78-L114","documentation":"This error comes from the rotating log Writer's Rotate method. After rotateLocked() closes the current file, the writer attempts to reopen the log file at w.path; if the reopen leaves file == nil, rotation succeeded in closing but failed to reopen, and I/O can no longer continue.","triggerScenarios":"Calling Rotate() when the underlying rotateLocked() implementation fails to recreate the log file (e.g. the log directory was deleted or permissions changed between close and reopen).","commonSituations":"Log directory removed or renamed while the daemon runs; permissions on the log directory changed after startup; disk removed/full so file creation fails silently inside rotateLocked; rotation triggered by SIGHUP or explicit call.","solutions":["Verify the log file's directory exists and is writable by the daemon process, then rotate again.","Recreate a missing log directory: mkdir -p <dir> && chown daemon-user <dir>.","Restart the daemon so the writer reinitializes with a fresh file handle.","Check filesystem errors (disk full, read-only mount) reported by the OS around the rotation time."],"exampleFix":"// before (dir removed while running)\n$ mv /var/log/cc-connect /var/log/cc-connect.bak\n// after\n$ mkdir -p /var/log/cc-connect && chmod 755 /var/log/cc-connect  # then re-trigger rotation","handlingStrategy":"try-catch","validationCode":"if st, err := os.Stat(filepath.Dir(w.Path())); err != nil || !st.IsDir() {\n    os.MkdirAll(filepath.Dir(w.Path()), 0o755)\n}","typeGuard":null,"tryCatchPattern":"if err := w.Rotate(); err != nil {\n    if errors.Is(err, os.ErrClosed) || strings.Contains(err.Error(), \"reopen after rotation failed\") {\n        // recreate log dir and reinitialize the writer\n        os.MkdirAll(filepath.Dir(w.Path()), 0o755)\n        w = daemon.NewRotateWriter(path, size, backups)\n    }\n    slog.Error(\"log rotation failed\", \"err\", err)\n}","preventionTips":["Never delete or rename the live log directory while the daemon runs; rotate files out of band instead.","Keep permissions on the log directory stable for the daemon user.","Monitor disk space so rotation-time file creation doesn't fail.","Re-create the writer after handling the error instead of continuing with a nil file."],"tags":["logging","filesystem","rotation"],"backgroundTag":"file-open-failed","analyzedSha":"4000b2338aa6e850c99df54f8b0ed6ed7460b401","analyzedAt":"2026-09-06T11:45:09.575Z","contentChangedAt":"2026-09-06T11:45:09.575Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}