gastownhall/beads · error

stat %s: %w

Error message

stat %s: %w

What it means

rotateLogIfOversized wraps os.Stat(primary) failures as "stat %s: %w". A missing log file is deliberately treated as a no-op (returns false, nil); this error is thrown only for other stat failures such as permission problems or I/O errors. It prevents bd from deciding whether the dolt-server log needs rotation.

Source

Thrown at internal/doltserver/logrotate.go:89

//
// Behavior:
//   - maxBytes <= 0: rotation disabled, returns (false, nil).
//   - primary missing: returns (false, nil). Nothing to rotate.
//   - primary size <= maxBytes: returns (false, nil). Leave alone.
//   - primary size > maxBytes: rename to <primary>.1 and return (true, nil).
//   - rename fails: returns (false, err).
//
// The caller should treat errors as non-fatal and proceed to open the log.
func rotateLogIfOversized(primary string, maxBytes int64) (bool, error) {
	if maxBytes <= 0 {
		return false, nil
	}
	info, err := os.Stat(primary)
	if err != nil {
		if os.IsNotExist(err) {
			return false, nil
		}
		return false, fmt.Errorf("stat %s: %w", primary, err)
	}
	if info.Size() <= maxBytes {
		return false, nil
	}
	rotated := rotatedLogPath(primary)
	// os.Rename overwrites the destination on both Unix and Windows (Go's
	// Rename wraps MoveFileEx with MOVEFILE_REPLACE_EXISTING on Windows).
	if err := os.Rename(primary, rotated); err != nil {
		return false, fmt.Errorf("rotating %s -> %s: %w", primary, rotated, err)
	}
	return true, nil
}

// maybeRotateLog is the convenience wrapper used by Start(). It rotates the
// dolt-server log if it is oversized and emits a debug message on both
// rotation and error paths. It never returns an error — rotation is
// best-effort and must not block server startup.
func maybeRotateLog(beadsDir string) {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Fix permissions on the log file and its directory so the bd user can stat it (chmod/chown).
  2. Verify the log path's parent directories exist and none of them is a regular file.
  3. Check disk health / mount state if the error is an I/O error (dmesg, mount output).
  4. If the path is wrong, correct the server log path configuration and restart.

Example fix

// before
info, err := os.Stat(primary)
if err != nil {
    if os.IsNotExist(err) {
        return false, nil
    }
    return false, fmt.Errorf("stat %s: %w", primary, err)
}
// after
// ensure the directory is writable by the bd user first:
//   sudo chown -R $(whoami) .beads/logs
// then retry the command that started the server
Defensive patterns

Strategy: validation

Validate before calling

if _, err := os.Stat(filepath.Dir(primary)); err != nil {
    return fmt.Errorf("log dir unavailable: %w", err)
}
if f, err := os.OpenFile(primary, os.O_APPEND|os.O_CREATE, 0o644); err == nil {
    f.Close()
}

Type guard

func logStatOk(path string) bool {
    _, err := os.Stat(path)
    return err == nil || os.IsNotExist(err) // both are safe for rotation
}

Try / catch

rotated, err := rotateLogIfOversized(primary, maxBytes)
if err != nil {
    if errors.Is(err, fs.ErrPermission) {
        log.Printf("cannot rotate log %s: fix permissions", primary)
    }
    return err
}

Prevention

When it happens

Trigger: maybeRotateLog calls rotateLogIfOversized and os.Stat on the primary dolt-server log fails with something other than NotExist: permission denied on the log directory, an I/O error, or a broken path component (e.g. parent is a file).

Common situations: The .beads/ or log directory was created by another user (root) and bd now runs unprivileged; the log path points across a flaky network mount; filesystem errors on disk-full volumes.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/43c22f1d6f5397e2. Report an issue: GitHub.