{"record":{"id":"f39b0a6dfc4dc6b3","repo":"gastownhall/beads","slug":"fs-writebeadsgitignore-w","errorCode":null,"errorMessage":"fs: WriteBeadsGitignore: %w","messagePattern":"fs: WriteBeadsGitignore: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/fs/beads.go","lineNumber":96,"sourceCode":"\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn false, nil\n\t}\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"fs: BeadsDirExists: stat %s: %w\", r.beadsDir, err)\n\t}\n\treturn info.IsDir(), nil\n}\n\nfunc (r *beadsDirFSRepositoryImpl) WriteBeadsGitignore(ctx context.Context) error {\n\tif r.templates.BeadsGitignore == \"\" {\n\t\treturn fmt.Errorf(\"fs: WriteBeadsGitignore: template not configured\")\n\t}\n\tpath := filepath.Join(r.beadsDir, \".gitignore\")\n\t// #nosec G304 -- path joined under bound beadsDir\n\texisting, err := os.ReadFile(path)\n\tif errors.Is(err, os.ErrNotExist) {\n\t\tif werr := os.WriteFile(path, []byte(r.templates.BeadsGitignore), 0600); werr != nil {\n\t\t\treturn fmt.Errorf(\"fs: WriteBeadsGitignore: %w\", werr)\n\t\t}\n\t\treturn nil\n\t}\n\tif err != nil {\n\t\treturn fmt.Errorf(\"fs: WriteBeadsGitignore: read: %w\", err)\n\t}\n\t// Existing file: append-only. A wholesale rewrite to the template\n\t// destroys local rules (e.g. export negations) the user added — the\n\t// gitignore is shared state, not bd-owned (bd-kaaz3).\n\tmissing := missingTemplatePatternLines(string(existing), r.templates.BeadsGitignore)\n\tif len(missing) == 0 {\n\t\treturn nil\n\t}\n\tcontent := string(existing)\n\tif len(content) > 0 && !strings.HasSuffix(content, \"\\n\") {\n\t\tcontent += \"\\n\"\n\t}\n\tcontent += \"\\n# Added by bd (missing required patterns)\\n\" + strings.Join(missing, \"\\n\") + \"\\n\"","sourceCodeStart":78,"sourceCodeEnd":114,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/fs/beads.go#L78-L114","documentation":"This wraps a failure from os.WriteFile when creating .beads/.gitignore for the first time (the file did not previously exist). bd could not write the new file with mode 0600, usually because the .beads directory is not writable or a non-directory entry blocks the path.","triggerScenarios":"Calling WriteBeadsGitignore when .beads/.gitignore does not exist and os.WriteFile(path, ..., 0600) fails: permission denied on the .beads dir, path is a directory, disk full, or read-only filesystem.","commonSituations":".beads mounted read-only (container volume); directory owned by another user; a directory named .gitignore sits in the way; quota exceeded on the volume.","solutions":["Ensure the .beads directory is writable by the current user (chown/chmod it)","Remove any directory or stale entry occupying the .gitignore path inside .beads","Free disk space / raise quota if the wrapped cause is ENOSPC","If the volume is read-only, remount read-write or run bd from a writable checkout"],"exampleFix":"// before\n$ sudo chown root .beads\n$ bd init  // fs: WriteBeadsGitignore: open .beads/.gitignore: permission denied\n// after\n$ sudo chown $(whoami) .beads\n$ bd init  # .gitignore created","handlingStrategy":"validation","validationCode":"if info, err := os.Stat(beadsDir); err == nil {\n\tif err := unix.Access(beadsDir, unix.W_OK); err != nil {\n\t\treturn fmt.Errorf(\"%s not writable: %v\", beadsDir, err)\n\t}\n\tif _, err := os.Lstat(filepath.Join(beadsDir, \".gitignore\")); err == nil {\n\t\tif fi, err := os.Stat(filepath.Join(beadsDir, \".gitignore\")); err == nil && fi.IsDir() {\n\t\t\treturn errors.New(\".beads/.gitignore is a directory\")\n\t\t}\n\t}\n}\nrepo.WriteBeadsGitignore(ctx)","typeGuard":"func isWriteErr(err error) bool {\n\tvar pe *fs.PathError\n\treturn errors.As(err, &pe) && (pe.Op == \"open\" || pe.Op == \"write\")\n}","tryCatchPattern":"if err := repo.WriteBeadsGitignore(ctx); err != nil {\n\tvar pe *fs.PathError\n\tif errors.As(err, &pe) && errors.Is(pe.Err, syscall.EACCES) {\n\t\treturn fmt.Errorf(\"make %s writable: chown -R $(whoami) %s\", filepath.Dir(pe.Path), filepath.Dir(pe.Path))\n\t}\n\treturn err\n}","preventionTips":["Keep .beads writable by the CI/agent user","Never mount .beads volumes read-only","Avoid placing directories named .gitignore inside .beads","Monitor disk quota on build agents"],"tags":["filesystem","write","gitignore","permissions"],"backgroundTag":"file-write-permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}