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
- Re-run the command — quarantineRecord retries with incremented timestamps on subsequent attempts
- Check permissions/locks on both source pidfile and workspace root; exclude workspace from AV real-time scanning
- Confirm rootDir is a single filesystem (no symlink/mount trickery causing EXDEV)
- 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
- Run one bd process per workspace at a time to avoid quarantine rename races
- Add workspace exclusions to Windows AV that locks files during rename
- Keep the workspace on a local filesystem supporting atomic rename
- Check wrapped errno (EACCES/EXDEV/ENOENT) to distinguish fixable permission issues from races
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
- failed to rename temp file: %w
- failed to migrate database %s to %s: %w
- retiring sidecar %s -> %s: %w
- target git directory identity changed
- target git marker identity changed
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/47a86df330d0b27e.
Report an issue: GitHub.