{"record":{"id":"0c50c7f2502329f2","repo":"gastownhall/beads","slug":"comparing-sidecars-s-and-s-w","errorCode":null,"errorMessage":"comparing sidecars %s and %s: %w","messagePattern":"comparing sidecars (.+?) and (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/migrate_hooks_apply.go","lineNumber":368,"sourceCode":"\t\tsourceExists, err := pathExists(op.SourcePath)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"checking source sidecar %s: %w\", op.SourcePath, err)\n\t\t}\n\t\tif !sourceExists {\n\t\t\tcontinue\n\t\t}\n\n\t\tdestinationExists, err := pathExists(op.DestinationPath)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"checking destination sidecar %s: %w\", op.DestinationPath, err)\n\t\t}\n\t\tif !destinationExists {\n\t\t\tcontinue\n\t\t}\n\n\t\tequal, err := filesEqual(op.SourcePath, op.DestinationPath)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"comparing sidecars %s and %s: %w\", op.SourcePath, op.DestinationPath, err)\n\t\t}\n\t\tif !equal {\n\t\t\treturn fmt.Errorf(\n\t\t\t\t\"artifact collision for %s: %s already exists with different content\",\n\t\t\t\top.SourcePath,\n\t\t\t\top.DestinationPath,\n\t\t\t)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc retireHookSidecar(op hookMigrationRetireOp) (string, error) {\n\tsourceExists, err := pathExists(op.SourcePath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"checking sidecar %s: %w\", op.SourcePath, err)\n\t}","sourceCodeStart":350,"sourceCodeEnd":386,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/migrate_hooks_apply.go#L350-L386","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\n-rw------- destination-sidecar  (unreadable by current user)\n// after\n$ chmod u+r /path/to/destination-sidecar\n$ bd migrate","handlingStrategy":"validation","validationCode":"for _, p := range []string{op.SourcePath, op.DestinationPath} {\n\tfi, err := os.Stat(p)\n\tif err != nil || fi.IsDir() {\n\t\treturn fmt.Errorf(\"%s is not a readable regular file\", p)\n\t}\n\tf, err := os.Open(p)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"%s not readable: %w\", p, err)\n\t}\n\tf.Close()\n}","typeGuard":null,"tryCatchPattern":"if err := validateRetireCollisionPolicy(ops); err != nil {\n\tvar pe *fs.PathError\n\tif errors.As(err, &pe) {\n\t\tlog.Printf(\"unreadable sidecar %s: %v\", pe.Path, pe.Err)\n\t}\n\treturn err\n}","preventionTips":["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"],"tags":["filesystem","io","migration","go"],"backgroundTag":"file-read-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}