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

  1. Verify the log file's directory exists and is writable by the daemon process, then rotate again.
  2. Recreate a missing log directory: mkdir -p <dir> && chown daemon-user <dir>.
  3. Restart the daemon so the writer reinitializes with a fresh file handle.
  4. 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

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


AI-assisted analysis of chenhg5/cc-connect@4000b2338a (2026-09-06). Data as JSON: /api/errors/f119eb371cba65c8. Report an issue: GitHub.