gastownhall/beads · warning
remove expired quarantine
Error message
remove expired quarantine %s: %w
What it means
sweepOldQuarantines removes .stale-<stamp> pidfile records older than quarantineRetention; this error wraps os.Remove failures for expired entries. Errors are accumulated and joined, so one bad file does not stop the sweep, but any failure surfaces here.
Solutions
- Fix ownership/permissions of the stale files: chown/chmod the workspace or remove them manually (rm <root>/*.stale-*)
- Re-run as the user that owns the stale records, or align user namespaces in containers
- On Windows, close tools/AV holding the file or add the workspace to exclusions
- Ignore if benign — the sweep error is non-fatal to spawning and the expired files can be cleaned manually
Example fix
// before
if err := os.Remove(filepath.Join(rootDir, entry.Name())); err != nil && !errors.Is(err, fs.ErrNotExist) {
errs = append(errs, fmt.Errorf("remove expired quarantine %s: %w", entry.Name(), err))
}
// after
if err := os.Remove(filepath.Join(rootDir, entry.Name())); err != nil && !errors.Is(err, fs.ErrNotExist) && !errors.Is(err, fs.ErrPermission) {
errs = append(errs, fmt.Errorf("remove expired quarantine %s: %w", entry.Name(), err))
} Defensive patterns
Strategy: try-catch
Try / catch
if err := sweepOldQuarantines(root, time.Now()); err != nil {
// joined errors: log and continue; spawn is still valid without the sweep
log.Printf("quarantine sweep skipped: %v", err)
} Prevention
- Run all bd usage of a workspace under one OS user so cleanup can remove old records
- Add the workspace to AV exclusions to prevent open-handle blocks on Windows
- Manually clear *.stale-* files from long-lived workspaces during maintenance
- Don't remount the workspace read-only while bd sessions are active
When it happens
Trigger: os.Remove on an expired quarantine file fails with a non-ErrNotExist error during spawnAndHandoff's retention sweep — typically permission denied or the file being held open by another process.
Common situations: Stale quarantine files owned by a different user (root-created records, then running bd as a normal user); AV/indexer holding the old file open on Windows; read-only remount of the workspace.
Related errors
- artifact(s) could not be removed
- failed to remove
- failed to walk directory tree
- remove dead spawn marker
- atomicfile: chmod
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/965b838f25729ca9.
Report an issue: GitHub.
Appendix: source
Thrown at internal/storage/dbproxy/proxy/endpoint.go:741
if entry.IsDir() {
continue
}
var stampText string
for _, prefix := range prefixes {
if strings.HasPrefix(entry.Name(), prefix) {
stampText = strings.TrimPrefix(entry.Name(), prefix)
break
}
}
if stampText == "" {
continue
}
stamp, err := strconv.ParseInt(stampText, 10, 64)
if err != nil || stamp >= cutoff {
continue
}
if err := os.Remove(filepath.Join(rootDir, entry.Name())); err != nil && !errors.Is(err, fs.ErrNotExist) {
errs = append(errs, fmt.Errorf("remove expired quarantine %s: %w", entry.Name(), err))
}
}
return errors.Join(errs...)
}
func cleanupOrphanBackend(rootDir string) error {
recordPath := pidfile.Path(rootDir, server.PIDFileName)
pf, readErr := pidfile.Read(rootDir, server.PIDFileName)
childLockPath := filepath.Join(rootDir, server.LockFileName)
childLock, lockErr := util.TryLock(childLockPath)
switch {
case lockErr == nil:
childLock.Unlock()
case lockfile.IsLocked(lockErr):
pid := 0
if pf != nil {
pid = pf.PidView on GitHub (pinned to 71377f2769)