gastownhall/beads · error
failed to read .gitignore: %w
Error message
failed to read .gitignore: %w
What it means
removeBeadsProjectGitignoreSection reads the repo-root .gitignore so it can strip the beads-managed section during stealth-mode cleanup. If os.ReadFile fails with anything other than fs.ErrNotExist (which is treated as "nothing to do" and returns nil), the underlying error is wrapped with %w and returned to applyFixList. This means the .gitignore exists in name but could not actually be read.
Source
Thrown at cmd/bd/init_stealth.go:178
}
return nil
}
// removeBeadsProjectGitignoreSection strips the bd-managed section from the tracked project-root
// .gitignore at repoPath, reversing doctor.EnsureProjectGitignore. It removes only the header beads
// writes (doctor.ProjectGitignoreHeader) plus the Dolt pattern lines beads added directly beneath
// it, so unrelated user patterns are preserved. If beads was the .gitignore's only content the file
// is removed entirely, restoring true stealth. Returns true when it changed (or removed) the file;
// a repo with no beads section (or no .gitignore) is left untouched.
func removeBeadsProjectGitignoreSection(repoPath string) (bool, error) {
gitignorePath := filepath.Join(repoPath, ".gitignore")
// #nosec G304 - path is the repo-root .gitignore
content, err := os.ReadFile(gitignorePath)
if err != nil {
if os.IsNotExist(err) {
return false, nil
}
return false, fmt.Errorf("failed to read .gitignore: %w", err)
}
managed := make(map[string]bool, len(doctor.ProjectGitignorePatterns))
for _, p := range doctor.ProjectGitignorePatterns {
managed[p] = true
}
lines := strings.Split(string(content), "\n")
out := make([]string, 0, len(lines))
changed := false
for i := 0; i < len(lines); i++ {
if strings.TrimSpace(lines[i]) == doctor.ProjectGitignoreHeader {
changed = true
// Drop the blank separator beads writes before the header, if we just emitted one.
if n := len(out); n > 0 && strings.TrimSpace(out[n-1]) == "" {
out = out[:n-1]
}
// Skip the header and the bd-managed pattern lines directly beneath it.View on GitHub (pinned to 71377f2769)
Solutions
- Check and fix file permissions: ls -l .gitignore, then chmod u+r .gitignore (or chown to the user running bd).
- Verify .gitignore is a regular file, not a directory or broken symlink: file .gitignore; remove/replace it if malformed.
- Re-run the command from the repository root so gitignorePath resolves to the correct .gitignore.
- If on a flaky network mount, retry after the mount is healthy, or copy the repo locally.
- As a last resort, recreate a minimal .gitignore with correct ownership and re-run bd stealth cleanup.
Example fix
// before (permissions blocking the read) $ ls -l .gitignore -rw------- 1 root root 412 .gitignore // after $ sudo chown $(whoami) .gitignore && chmod 644 .gitignore $ bd doctor --fix # removeBeadsProjectGitignoreSection now reads the file
Defensive patterns
Strategy: validation
Validate before calling
info, err := os.Stat(gitignorePath)
if err == nil && !info.IsDir() {
if f, err := os.OpenFile(gitignorePath, os.O_RDONLY, 0); err == nil {
f.Close() // readable; removeBeadsProjectGitignoreSection will not fail on read
} else {
fmt.Printf(".gitignore not readable: %v\n", err)
}
} Try / catch
changed, err := removeBeadsProjectGitignoreSection(gitignorePath)
if err != nil {
var perr *fs.PathError
if errors.As(err, &perr) && errors.Is(perr.Err, syscall.EACCES) {
return fmt.Errorf("cannot read %s: check file permissions/ownership: %w", gitignorePath, err)
}
return err
} Prevention
- Run bd commands as the same user that owns the repository files; avoid mixing sudo'd and normal runs.
- Keep .gitignore at 0644 with normal user ownership in every clone.
- Never replace .gitignore with a directory or dangling symlink.
- Avoid editing repos on flaky network mounts; work on local clones.
When it happens
Trigger: os.ReadFile(gitignorePath) returns an error that is not os.IsNotExist: permission denied on the file, the path is a directory instead of a file, an I/O error occurs, or the path is too long / on a failing mount.
Common situations: Running `bd doctor` / stealth fix under a different user than the one who owns .gitignore (e.g. sudo'd agent runs); .gitignore has restrictive modes (0600 owned by root); CI containers where the repo was mounted with odd permissions; a stale symlink pointing at a nonexistent or unreadable target (symlink errors are not ErrNotExist in all cases); NFS/Network mounts dropping reads.
Related errors
- failed to remove emptied .gitignore: %w
- failed to write .gitignore: %w
- ensure .beads/.gitignore: %w
- failed to inspect target repo %s: %w
- open dependency file: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/4c4241af494aee13.
Report an issue: GitHub.