{"record":{"id":"15459a1ecaedaf5b","repo":"gastownhall/beads","slug":"failed-to-write-gitignore-w","errorCode":null,"errorMessage":"failed to write .gitignore: %w","messagePattern":"failed to write \\.gitignore: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/doctor/gitignore.go","lineNumber":800,"sourceCode":"\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\n\tnewContent += \"\\n\" + ProjectGitignoreHeader + \"\\n\"\n\tfor _, pattern := range toAdd {\n\t\tnewContent += pattern + \"\\n\"\n\t}\n\n\t// #nosec G306 -- gitignore needs to be readable by git and collaborators\n\tif err := os.WriteFile(gitignorePath, []byte(newContent), 0644); err != nil {\n\t\treturn fmt.Errorf(\"failed to write .gitignore: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// FixProjectGitignore is an alias for EnsureProjectGitignore, used by bd doctor --fix.\n// repoPath is the project root directory.\nfunc FixProjectGitignore(repoPath string) error {\n\treturn EnsureProjectGitignore(repoPath)\n}\n\n// containsGitignorePattern checks if a gitignore file content contains the given pattern.\n// It checks for the pattern as a standalone line (ignoring leading/trailing whitespace).\nfunc containsGitignorePattern(content, pattern string) bool {\n\tfor _, line := range strings.Split(content, \"\\n\") {\n\t\tline = strings.TrimSpace(line)\n\t\tif line == pattern {\n\t\t\treturn true","sourceCodeStart":782,"sourceCodeEnd":818,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/doctor/gitignore.go#L782-L818","documentation":"After computing which ProjectGitignorePatterns are missing, EnsureProjectGitignore writes the updated content with os.WriteFile (mode 0644). Any write failure is wrapped in this error. It means the tool read the file fine but could not persist the updated .gitignore.","triggerScenarios":"os.WriteFile(gitignorePath, []byte(newContent), 0644) returns a non-nil error — permission denied, read-only filesystem, disk full, or the target path is a directory.","commonSituations":"Running bd doctor --fix in a repo owned by root or another user; read-only CI checkout; disk-full; .gitignore replaced by a directory; immutable attribute set on the file.","solutions":["Fix write permissions: sudo chown $(whoami) .gitignore or chmod u+w .gitignore.","Ensure the repo/filesystem is writable (not a read-only mount or CI artifact checkout).","Free disk space if the filesystem is full (df -h).","If .gitignore is a directory, replace it with a regular file and retry."],"exampleFix":"// before\n$ bd doctor --fix\nError: failed to write .gitignore: open /repo/.gitignore: permission denied\n// after\n$ chmod u+w .gitignore   # or: sudo chown $(whoami) .gitignore\n$ bd doctor --fix\n✓ .gitignore updated","handlingStrategy":"validation","validationCode":"info, err := os.Stat(gitignorePath)\nif err == nil && info.IsDir() {\n    return fmt.Errorf(\"precheck: .gitignore is a directory, not a file\")\n}\nif f, err := os.OpenFile(gitignorePath, os.O_WRONLY|os.O_CREATE, 0644); err != nil {\n    return fmt.Errorf(\"precheck: .gitignore not writable: %w\", err)\n} else {\n    f.Close()\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) || errors.Is(perr.Err, syscall.EROFS)) {\n        // prompt for elevation or switch to a writable checkout\n    }\n    return err\n}","preventionTips":["Run bd doctor --fix with write access to the repo (avoid root-owned checkouts).","Use rw checkouts in CI when automated fixes are expected.","Monitor disk space before running write-heavy maintenance.","Keep .gitignore a regular file owned by the working user."],"tags":["filesystem","gitignore","permissions","write"],"backgroundTag":"file-write-permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}