gastownhall/beads · error
removing already-retired sidecar %s: %w
Error message
removing already-retired sidecar %s: %w
What it means
This wraps os.Remove failure when deleting the source sidecar that has already been confirmed identical to an existing destination. The migration verified both files match, but unlinking the redundant source failed. The destination file remains intact; the source is left in place.
Source
Thrown at cmd/bd/migrate_hooks_apply.go:405
if !sourceExists {
return "", nil
}
destinationExists, err := pathExists(op.DestinationPath)
if err != nil {
return "", fmt.Errorf("checking sidecar destination %s: %w", op.DestinationPath, err)
}
if destinationExists {
equal, err := filesEqual(op.SourcePath, op.DestinationPath)
if err != nil {
return "", fmt.Errorf("comparing sidecar %s to %s: %w", op.SourcePath, op.DestinationPath, err)
}
if !equal {
return "", fmt.Errorf("artifact collision for %s: %s already exists with different content", op.SourcePath, op.DestinationPath)
}
if err := os.Remove(op.SourcePath); err != nil {
return "", fmt.Errorf("removing already-retired sidecar %s: %w", op.SourcePath, err)
}
return op.SourcePath + " -> " + op.DestinationPath + " (destination already existed)", nil
}
if err := os.Rename(op.SourcePath, op.DestinationPath); err != nil {
return "", fmt.Errorf("retiring sidecar %s -> %s: %w", op.SourcePath, op.DestinationPath, err)
}
return op.SourcePath + " -> " + op.DestinationPath, nil
}
func pathExists(path string) (bool, error) {
_, err := os.Stat(path)
if err == nil {
return true, nil
}
if errors.Is(err, os.ErrNotExist) {
return false, nilView on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped error and permissions on the source's parent directory (write permission is what matters).
- chmod u+w the parent directory, or remove the immutable flag: chattr -i <file>.
- On Windows, close processes holding the file (AV, editors) and retry.
- Since destination content is already identical, manually deleting the source and re-running is safe.
Example fix
// before dr-xr-xr-x /path/to/dir // after $ chmod u+w /path/to/dir $ bd migrate
Defensive patterns
Strategy: validation
Validate before calling
fi, err := os.Stat(filepath.Dir(op.SourcePath))
if err != nil {
return err
}
if fi.Mode()&0o200 == 0 {
return fmt.Errorf("source dir %s not writable; os.Remove will fail", filepath.Dir(op.SourcePath))
} Try / catch
if _, err := retireHookSidecar(op); err != nil {
var pe *fs.PathError
if errors.As(err, &pe) {
return fmt.Errorf("cannot delete %s (check dir write perms / file locks): %w", pe.Path, pe.Err)
}
return err
} Prevention
- Ensure write permission on the directory containing the source sidecar
- Clear the immutable flag if set: chattr -i <file>
- On Windows, close editors/AV holding the file before migrating
When it happens
Trigger: During `bd migrate` apply, os.Remove(op.SourcePath) fails — source directory lacks write permission (removing a file requires write on its directory), the file was locked by another process on Windows, or the directory is read-only.
Common situations: Source directory mounted read-only; parent directory owned by another user (no +w); Windows AV/indexer holding the file open; immutable attribute set on the file.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- checking destination sidecar %s: %w
- checking sidecar %s: %w
- checking sidecar destination %s: %w
- dolt path is not executable
- failed to create backup directory: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/2ee022958576e814.
Report an issue: GitHub.