gastownhall/beads · error
comparing sidecar %s to %s: %w
Error message
comparing sidecar %s to %s: %w
What it means
This wraps a failure from filesEqual when the destination sidecar already exists and retirement must verify the two are identical before deleting the source. A read error on either file aborts the retire step. This is an I/O problem, not a content conflict.
Source
Thrown at cmd/bd/migrate_hooks_apply.go:399
func retireHookSidecar(op hookMigrationRetireOp) (string, error) {
sourceExists, err := pathExists(op.SourcePath)
if err != nil {
return "", fmt.Errorf("checking sidecar %s: %w", op.SourcePath, err)
}
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) {View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped error and `ls -la` both paths to confirm they are readable regular files.
- Fix read permissions: chmod u+r on the unreadable file.
- If the path is a directory or otherwise wrong, remove/relocate it manually and re-run.
- Retry the migration after the interfering process (AV/backup) finishes.
Example fix
// before -rw------- source-sidecar (other user's file) // after $ chmod u+r /path/to/source-sidecar $ bd migrate
Defensive patterns
Strategy: validation
Validate before calling
for _, p := range []string{op.SourcePath, op.DestinationPath} {
f, err := os.Open(p)
if err != nil {
return fmt.Errorf("%s unreadable: %w", p, err)
}
f.Close()
} Try / catch
if _, err := retireHookSidecar(op); err != nil {
var pe *fs.PathError
if errors.As(err, &pe) && errors.Is(pe.Err, syscall.EACCES) {
// grant read permission or stop interfering processes
}
return err
} Prevention
- Pause AV/backup/indexer jobs that lock hook files during migration
- Keep sidecar files on local, healthy storage
- Don't run concurrent bd migrations in the same repo
When it happens
Trigger: During `bd migrate` apply, destination exists and os.ReadFile on the source or destination fails — file became unreadable after the exists-check (race), the path is actually a directory, or a disk read error occurs.
Common situations: Antivirus/backup tool locked or chmod'ed the file mid-run; files on flaky external storage; destination was swapped for a directory by another process.
Related errors
- comparing sidecars %s and %s: %w
- failed to read backup state: %w
- failed to write temp file: %w
- failed to sync temp file: %w
- failed to write issue %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/4f366bb3154d9d8b.
Report an issue: GitHub.