{"record":{"id":"69266ae1cfb291ec","repo":"gastownhall/beads","slug":"scanning-artifacts-at-s-w","errorCode":null,"errorMessage":"scanning artifacts at %s: %w","messagePattern":"scanning artifacts at (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/doctor/artifacts.go","lineNumber":129,"sourceCode":"\n\t\t// Skip node_modules and similar\n\t\tif info.IsDir() && (base == \"node_modules\" || base == \"vendor\" || base == \"__pycache__\") {\n\t\t\treturn filepath.SkipDir\n\t\t}\n\n\t\t// We only care about directories named \".beads\"\n\t\tif !info.IsDir() || base != \".beads\" {\n\t\t\treturn nil\n\t\t}\n\n\t\t// Found a .beads directory - scan it\n\t\tscanBeadsDir(path, &report)\n\n\t\t// Don't descend into .beads/ itself (we've scanned it)\n\t\treturn filepath.SkipDir\n\t})\n\tif walkErr != nil {\n\t\treturn report, fmt.Errorf(\"scanning artifacts at %s: %w\", rootPath, walkErr)\n\t}\n\n\treport.TotalCount = len(report.SQLiteArtifacts) +\n\t\tlen(report.CruftBeadsDirs) + len(report.RedirectIssues)\n\n\tfor _, findings := range [][]ArtifactFinding{\n\t\treport.SQLiteArtifacts,\n\t\treport.CruftBeadsDirs, report.RedirectIssues,\n\t} {\n\t\tfor _, f := range findings {\n\t\t\tif f.SafeDelete {\n\t\t\t\treport.SafeDeleteCount++\n\t\t\t}\n\t\t}\n\t}\n\n\treturn report, nil\n}","sourceCodeStart":111,"sourceCodeEnd":147,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/doctor/artifacts.go#L111-L147","documentation":"ScanForArtifacts wraps errors returned by filepath.WalkDir while scanning the repository tree for leftover SQLite artifacts, cruft .beads directories, and redirect issues. It reports that the filesystem walk itself failed (not an individual file finding).","triggerScenarios":"ScanForArtifacts (via CheckClassicArtifacts during `bd doctor`) when filepath.WalkDir returns a non-SkipDir error — e.g. unreadable directories, permission-denied on a subtree, symlink loops, or deleted dirs mid-walk — at cmd/bd/doctor/artifacts.go:129.","commonSituations":"Scanning a repo containing root-owned or permission-restricted directories, a broken/looping symlink, or a network mount that drops out during the walk; also from tests that feed ScanForArtifacts odd fixture trees.","solutions":["Fix permissions on unreadable directories reported in the wrapped error (chmod/chown or run with adequate privileges)","Remove or repair broken symlinks in the tree being scanned","Exclude exotic mounts from the scan by running bd doctor inside the project directory rather than above it","Retry after fixing; then re-run `bd doctor` to confirm the artifact scan completes"],"exampleFix":"// before\nchmod 000 ./node_modules/.cache/locked-dir\n// after\nchmod u+rX ./node_modules/.cache/locked-dir","handlingStrategy":"retry","validationCode":"import \"os\"\nfunc canWalk(root string) error {\n\treturn filepath.WalkDir(root, func(p string, d os.DirEntry, err error) error {\n\t\tif err != nil { return err }\n\t\tif d.IsDir() {\n\t\t\tif f, ferr := os.Open(p); ferr != nil { return ferr } else { f.Close() }\n\t\t}\n\t\treturn nil\n\t})\n}","typeGuard":null,"tryCatchPattern":"report, err := ScanForArtifacts(root)\nif err != nil {\n\tvar pe *fs.PathError\n\tif errors.As(err, &pe) {\n\t\tlog.Printf(\"scan blocked at %s: %v — fix perms and retry\", pe.Path, pe.Err)\n\t}\n\treturn err\n}","preventionTips":["Keep repository directories world-readable for CI/agent users","Remove broken symlinks before running doctor scans","Run scans on local filesystems, not flaky network mounts","Note SkipDir is used internally; never return arbitrary walk errors from custom walkers"],"tags":["filesystem","walk","doctor","artifacts"],"backgroundTag":"file-walk-permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}