{"record":{"id":"6498d4f6a2670bc4","repo":"gastownhall/beads","slug":"failed-to-read-gitignore-w","errorCode":null,"errorMessage":"failed to read .gitignore: %w","messagePattern":"failed to read \\.gitignore: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/doctor/gitignore.go","lineNumber":774,"sourceCode":"\t\tStatus:  StatusOK,\n\t\tMessage: \"Dolt and credential files excluded\",\n\t}\n}\n\n// EnsureProjectGitignore adds .dolt/, *.db, and .beads-credential-key patterns\n// to the project-root .gitignore if they are not already present. Creates the\n// file if it doesn't exist. This prevents users from accidentally committing\n// Dolt database files or the credential encryption key.\n// repoPath is the project root directory.\nfunc EnsureProjectGitignore(repoPath string) error {\n\tgitignorePath := filepath.Join(repoPath, \".gitignore\")\n\n\tvar existingContent string\n\t// #nosec G304 -- path is hardcoded\n\tif content, err := os.ReadFile(gitignorePath); err == nil {\n\t\texistingContent = string(content)\n\t} else if !os.IsNotExist(err) {\n\t\treturn fmt.Errorf(\"failed to read .gitignore: %w\", err)\n\t}\n\n\tvar toAdd []string\n\tfor _, pattern := range ProjectGitignorePatterns {\n\t\tif !containsGitignorePattern(existingContent, pattern) {\n\t\t\ttoAdd = append(toAdd, pattern)\n\t\t}\n\t}\n\n\tif len(toAdd) == 0 {\n\t\treturn nil // All patterns already present\n\t}\n\n\tnewContent := existingContent\n\tif len(newContent) > 0 && !strings.HasSuffix(newContent, \"\\n\") {\n\t\tnewContent += \"\\n\"\n\t}\n","sourceCodeStart":756,"sourceCodeEnd":792,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/doctor/gitignore.go#L756-L792","documentation":"EnsureProjectGitignore reads the project's existing .gitignore so it can append only missing patterns. If os.ReadFile fails with any error other than NotExist (which is treated as 'no file yet'), it wraps and returns this error. It signals an unexpected filesystem problem, not a missing file.","triggerScenarios":"os.ReadFile(gitignorePath) returns an error and !os.IsNotExist(err) — e.g. a .gitignore path that is a directory, or a permission-denied read.","commonSituations":".gitignore exists but the process lacks read permission; .gitignore is actually a directory (often from a bad checkout or symlink); filesystem/ACL issues on network mounts; SELinux or sandbox policies blocking reads in CI containers.","solutions":["Check permissions: ls -la .gitignore and chmod u+r (or chown) so the running user can read it.","If .gitignore is a directory, remove or rename it (mv .gitignore .gitignore.bak) and re-run bd doctor --fix.","Run inside the correct repo directory; a wrong cwd can point gitignorePath at an unusable path.","If on a network/odd filesystem, copy the repo locally and retry."],"exampleFix":"// before\n$ bd doctor --fix\nError: failed to read .gitignore: open /repo/.gitignore: is a directory\n// after\n$ mv -f .gitignore .gitignore.bak\n$ bd doctor --fix\n✓ .gitignore updated","handlingStrategy":"validation","validationCode":"info, err := os.Stat(gitignorePath)\nif err != nil && !os.IsNotExist(err) {\n    return fmt.Errorf(\"precheck: cannot stat .gitignore: %w\", err)\n}\nif info != nil && info.IsDir() {\n    return fmt.Errorf(\"precheck: .gitignore is a directory\")\n}","typeGuard":null,"tryCatchPattern":"if _, err := doctor.EnsureProjectGitignore(dir); err != nil {\n    var perr *fs.PathError\n    if errors.As(err, &perr) && errors.Is(perr.Err, syscall.EACCES) {\n        // handle permission problem (chown/chmod or elevate)\n    }\n    return err\n}","preventionTips":["Run bd doctor as a user with read access to the repo.","Never replace .gitignore with a directory or broken symlink.","Check file health (ls -la .gitignore) before automated fixes on odd filesystems."],"tags":["filesystem","gitignore","permissions"],"backgroundTag":"gitignore-read-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}