gastownhall/beads · error

rename %s to %s: %w

Error message

rename %s to %s: %w

What it means

quarantineRecord renames the source pidfile to the chosen <name>.stale-<unix-ts> target; this error wraps an os.Rename failure. The old proxy record is preserved but not moved, so the caller (spawn path) aborts the restart.

Source

Thrown at internal/storage/dbproxy/proxy/endpoint.go:705

	target, err := quarantineRecord(rootDir, PIDFileName, time.Now())
	if err != nil {
		return fmt.Errorf("quarantine proxy record: %w", err)
	}
	log.Printf("dbproxy: preserved proxy record as %s", target)
	return nil
}

func quarantineRecord(rootDir, name string, now time.Time) (string, error) {
	source := pidfile.Path(rootDir, name)
	for stamp := now.Unix(); ; stamp++ {
		target := filepath.Join(rootDir, name+".stale-"+strconv.FormatInt(stamp, 10))
		if _, err := os.Lstat(target); err == nil {
			continue
		} else if !errors.Is(err, fs.ErrNotExist) {
			return "", fmt.Errorf("inspect quarantine target %s: %w", target, err)
		}
		if err := os.Rename(source, target); err != nil {
			return "", fmt.Errorf("rename %s to %s: %w", source, target, err)
		}
		return target, nil
	}
}

func sweepOldQuarantines(rootDir string, now time.Time) error {
	entries, err := os.ReadDir(rootDir)
	if err != nil {
		return err
	}
	cutoff := now.Add(-quarantineRetention).Unix()
	prefixes := []string{
		PIDFileName + ".stale-",
		server.PIDFileName + ".stale-",
	}
	var errs []error
	for _, entry := range entries {
		if entry.IsDir() {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Re-run the command — quarantineRecord retries with incremented timestamps on subsequent attempts
  2. Check permissions/locks on both source pidfile and workspace root; exclude workspace from AV real-time scanning
  3. Confirm rootDir is a single filesystem (no symlink/mount trickery causing EXDEV)
  4. Check the wrapped errno via errors.Unwrap for the specific OS error and fix accordingly

Example fix

// before
if err := os.Rename(source, target); err != nil {
    return "", fmt.Errorf("rename %s to %s: %w", source, target, err)
}
// after
if err := os.Rename(source, target); err != nil {
    if errors.Is(err, fs.ErrNotExist) { // source vanished: racing shutdown won
        return "", nil // nothing to quarantine
    }
    return "", fmt.Errorf("rename %s to %s: %w", source, target, err)
}
Defensive patterns

Strategy: retry

Validate before calling

src := pidfile.Path(root, PIDFileName)
if _, err := os.Stat(src); err != nil {
    return fmt.Errorf("pidfile gone before quarantine: %w", err)
}

Try / catch

for attempt := 0; attempt < 3; attempt++ {
    target, err := quarantineRecord(root, PIDFileName, time.Now())
    if err == nil { break }
    if errors.Is(err, fs.ErrPermission) || errors.Is(err, syscall.EXDEV) { return err } // non-retryable
    time.Sleep(time.Duration(attempt+1) * 50 * time.Millisecond)
}

Prevention

When it happens

Trigger: os.Rename(source, target) fails after Lstat reported the target as non-existent — typical causes: target created by a racing process in the gap, source file removed concurrently, permission denied, or cross-device link if rootDir spans mounts.

Common situations: Two bd processes quarantining simultaneously; antivirus locking the pidfile on Windows (ACCESS_DENIED); pidfile deleted by a concurrent shutdown between Lstat and Rename; workspace on a network mount with flaky rename semantics.

Related errors


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