chenhg5/cc-connect · error
logrotate: reopen after rotation failed for %s
Error message
logrotate: reopen after rotation failed for %s
What it means
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.
Source
Thrown at daemon/logrotate.go:96
w.rotateLocked()
}
return n, err
}
// Rotate forces a rotation regardless of the current size. Useful for
// tests and for SIGHUP-style "start a new log file" hooks. Errors are
// logged via slog but never returned, because Write cannot surface
// rotation errors to its caller and the alternative (dropping log
// data) is worse than a missed rotation.
func (w *RotatingWriter) Rotate() error {
w.mu.Lock()
defer w.mu.Unlock()
if w.file == nil {
return os.ErrClosed
}
w.rotateLocked()
if w.file == nil {
return fmt.Errorf("logrotate: reopen after rotation failed for %s", w.path)
}
return nil
}
// backupPath returns the rotated-file name for the i-th backup.
// .1 is the most recent, .N is the oldest.
func (w *RotatingWriter) backupPath(i int) string {
return fmt.Sprintf("%s.%d", w.path, i)
}
// rotateLocked performs the chain rotation: delete the oldest (.N),
// shift .(N-1) -> .N, ... .1 -> .2, rename active -> .1, reopen.
//
// Caller must hold w.mu.
func (w *RotatingWriter) rotateLocked() {
w.file.Close()
// 1. Delete the oldest, if it exists. If this fails it is not fatal —View on GitHub (pinned to 4000b2338a)
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.
Example fix
// before (dir removed while running) $ mv /var/log/cc-connect /var/log/cc-connect.bak // after $ mkdir -p /var/log/cc-connect && chmod 755 /var/log/cc-connect # then re-trigger rotation
Defensive patterns
Strategy: try-catch
Validate before calling
if st, err := os.Stat(filepath.Dir(w.Path())); err != nil || !st.IsDir() {
os.MkdirAll(filepath.Dir(w.Path()), 0o755)
} Try / catch
if err := w.Rotate(); err != nil {
if errors.Is(err, os.ErrClosed) || strings.Contains(err.Error(), "reopen after rotation failed") {
// recreate log dir and reinitialize the writer
os.MkdirAll(filepath.Dir(w.Path()), 0o755)
w = daemon.NewRotateWriter(path, size, backups)
}
slog.Error("log rotation failed", "err", err)
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.
Related errors
- create log dir: %w
- create Agy permission overlay: %w
- read existing Agy hooks %s: %w
- write Agy hooks overlay: %w
- read Agy config directory %s: %w
AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06).
Data as JSON: /api/errors/f119eb371cba65c8.
Report an issue: GitHub.