{"record":{"id":"a16d3c2b62211663","repo":"gastownhall/beads","slug":"checking-sidecar-s-w","errorCode":null,"errorMessage":"checking sidecar %s: %w","messagePattern":"checking sidecar (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/migrate_hooks_apply.go","lineNumber":385,"sourceCode":"\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}\n\tif !sourceExists {\n\t\treturn \"\", nil\n\t}\n\n\tdestinationExists, err := pathExists(op.DestinationPath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"checking sidecar destination %s: %w\", op.DestinationPath, err)\n\t}\n\n\tif destinationExists {\n\t\tequal, err := filesEqual(op.SourcePath, op.DestinationPath)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"comparing sidecar %s to %s: %w\", op.SourcePath, op.DestinationPath, err)\n\t\t}\n\t\tif !equal {\n\t\t\treturn \"\", fmt.Errorf(\"artifact collision for %s: %s already exists with different content\", op.SourcePath, op.DestinationPath)\n\t\t}","sourceCodeStart":367,"sourceCodeEnd":403,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/migrate_hooks_apply.go#L367-L403","documentation":"This wraps an os.Stat failure on the retire op's source sidecar during retirement. pathExists distinguishes only 'exists' vs os.ErrNotExist; any other stat error (permission, I/O) surfaces here. The retire step aborts mid-apply, so earlier operations may already have been applied.","triggerScenarios":"During `bd migrate` apply, os.Stat(op.SourcePath) fails with an error other than os.ErrNotExist — e.g. the source sidecar's parent directory became inaccessible, or the path sits on a failed mount.","commonSituations":"Permissions on the source directory changed after validation passed; an NFS/removable mount dropped mid-run; security software blocked the stat.","solutions":["Read the wrapped error and stat the source path manually to identify the failing component.","Restore access to the source directory (chown/chmod or remount).","Re-run the migration once access is restored — already-completed retire ops are idempotent (missing sources are skipped)."],"exampleFix":"// before\n$ ls -ld /path/to/source-dir\nperms: root-only\n// after\n$ sudo chmod a+rx /path/to/source-dir\n$ bd migrate","handlingStrategy":"validation","validationCode":"if _, err := os.Stat(filepath.Dir(op.SourcePath)); err != nil {\n\treturn fmt.Errorf(\"source dir not accessible: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if _, err := retireHookSidecar(op); err != nil {\n\tvar pe *fs.PathError\n\tif errors.As(err, &pe) && errors.Is(pe.Err, os.ErrPermission) {\n\t\t// surface a permission-fix hint\n\t}\n\treturn err\n}","preventionTips":["Run bd as a user with write access to the repo's hook directories","Keep source sidecar files on a reliably mounted local filesystem","Re-run migrate after fixing access; retire ops are idempotent"],"tags":["filesystem","permissions","migration","go"],"backgroundTag":"permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}