gastownhall/beads · error
comparing sidecars %s and %s: %w
Error message
comparing sidecars %s and %s: %w
What it means
This error wraps a failure from filesEqual, which reads both sidecar files fully with os.ReadFile and byte-compares them during collision validation. It means one or both files could not be read (permissions, missing after the earlier exists-check, or I/O error). Validation aborts before any migration is applied.
Source
Thrown at cmd/bd/migrate_hooks_apply.go:368
sourceExists, err := pathExists(op.SourcePath)
if err != nil {
return fmt.Errorf("checking source sidecar %s: %w", op.SourcePath, err)
}
if !sourceExists {
continue
}
destinationExists, err := pathExists(op.DestinationPath)
if err != nil {
return fmt.Errorf("checking destination sidecar %s: %w", op.DestinationPath, err)
}
if !destinationExists {
continue
}
equal, err := filesEqual(op.SourcePath, op.DestinationPath)
if err != nil {
return fmt.Errorf("comparing sidecars %s and %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,
)
}
}
return nil
}
func retireHookSidecar(op hookMigrationRetireOp) (string, error) {
sourceExists, err := pathExists(op.SourcePath)
if err != nil {
return "", fmt.Errorf("checking sidecar %s: %w", op.SourcePath, err)
}View on GitHub (pinned to 71377f2769)
Solutions
- Check the wrapped error and run `ls -la` on both paths to see what they actually are (file vs directory, permissions).
- If either path is a directory, remove or relocate it manually and re-run.
- Grant read permission on both files: chmod u+r on the source and destination.
- Re-run the migration; transient races usually resolve on a second run.
Example fix
// before -rw------- destination-sidecar (unreadable by current user) // after $ chmod u+r /path/to/destination-sidecar $ bd migrate
Defensive patterns
Strategy: validation
Validate before calling
for _, p := range []string{op.SourcePath, op.DestinationPath} {
fi, err := os.Stat(p)
if err != nil || fi.IsDir() {
return fmt.Errorf("%s is not a readable regular file", p)
}
f, err := os.Open(p)
if err != nil {
return fmt.Errorf("%s not readable: %w", p, err)
}
f.Close()
} Try / catch
if err := validateRetireCollisionPolicy(ops); err != nil {
var pe *fs.PathError
if errors.As(err, &pe) {
log.Printf("unreadable sidecar %s: %v", pe.Path, pe.Err)
}
return err
} Prevention
- Keep sidecar files readable by the user running bd
- Don't let other processes chmod or delete sidecar files during migration
- Verify sidecar paths are regular files, not directories or fifos
When it happens
Trigger: During `bd migrate` collision validation, both source and destination sidecar paths exist per os.Stat, but os.ReadFile on either path fails — e.g. file permissions changed between the stat and read, the path is a directory, or a read I/O error occurs.
Common situations: A file deleted or chmod'ed between the exists check and the compare (race with another process), stat succeeding on a directory instead of a file, or a file unreadable by the current user despite directory access.
Related errors
- comparing sidecar %s to %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/0c50c7f2502329f2.
Report an issue: GitHub.