{"record":{"id":"4e3352c5e7553d97","repo":"gastownhall/beads","slug":"writing-config-w","errorCode":null,"errorMessage":"writing config: %w","messagePattern":"writing config: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/configfile/configfile.go","lineNumber":172,"sourceCode":"func (c *Config) Save(beadsDir string) error {\n\tconfigPath := ConfigPath(beadsDir)\n\n\tsaved := *c\n\tif filepath.IsAbs(saved.DoltDataDir) {\n\t\tsaved.DoltDataDir = \"\"\n\t}\n\n\tdata, err := json.MarshalIndent(&saved, \"\", \"  \")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"marshaling config: %w\", err)\n\t}\n\n\t// Write-temp-then-rename: a plain os.WriteFile truncates in place, so a\n\t// concurrent Load can observe an empty or partial metadata.json and feed\n\t// store selection a corrupt config. Rename within the same directory is\n\t// atomic, so readers see either the old or the new file, never a torn one.\n\tif err := writeFileAtomic(configPath, data, 0o600); err != nil {\n\t\treturn fmt.Errorf(\"writing config: %w\", err)\n\t}\n\n\treturn nil\n}\n\n// writeFileAtomic writes data to a temp file in path's directory and renames\n// it over path, so concurrent readers never observe a truncated or partial\n// file.\nfunc writeFileAtomic(path string, data []byte, perm os.FileMode) error {\n\tdir := filepath.Dir(path)\n\ttmp, err := os.CreateTemp(dir, filepath.Base(path)+\".tmp-*\")\n\tif err != nil {\n\t\treturn err\n\t}\n\ttmpName := tmp.Name()\n\tdefer os.Remove(tmpName) //nolint:errcheck // no-op after successful rename\n\tif _, err := tmp.Write(data); err != nil {\n\t\t_ = tmp.Close()","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/configfile/configfile.go#L154-L190","documentation":"Config.Save() writes metadata.json via writeFileAtomic (temp file + rename) to guarantee readers never see a torn file. This error wraps any failure in that sequence: temp-file creation, write, chmod, close, or rename — typically environment/permission issues in the beads directory, not JSON problems.","triggerScenarios":"Calling Config.Save(beadsDir) when the beads directory is not writable, the disk is full, the directory was removed mid-save, or the target is on a filesystem where rename semantics fail (e.g. some network mounts, cross-device issues, or immutable files).","commonSituations":"Read-only .beads (mounted volume, checkout permissions); quota exceeded; metadata.json made immutable (chattr +i) or owned by root; NFS with stale handles; concurrent runs on a filesystem lacking atomic rename within the same directory.","solutions":["Verify the beads directory is writable by the current user (touch .beads/.probe) and fix with chown/chmod","Free disk space / raise quota if the filesystem is full","Remove immutability flags (chattr -i) or stale metadata.json.tmp-* files","If on NFS or unusual mounts, move the workspace to a local filesystem or stop concurrent bd processes"],"exampleFix":"// before\ndr-xr-xr-x 2 user user .beads\n// after\n$ chmod u+w .beads && bd init","handlingStrategy":"try-catch","validationCode":"info, err := os.Stat(beadsDir)\nif err != nil || !info.IsDir() {\n\treturn fmt.Errorf(\"beads dir missing\")\n}\nprobe := filepath.Join(beadsDir, \".probe\")\nif f, err := os.Create(probe); err != nil {\n\treturn fmt.Errorf(\"beads dir not writable: %w\", err)\n} else {\n\tf.Close(); os.Remove(probe)\n}","typeGuard":null,"tryCatchPattern":"if err := cfg.Save(beadsDir); err != nil {\n\tvar pathErr *os.PathError\n\tif errors.As(err, &pathErr) {\n\t\t// temp-create/rename failure: check space, perms, mount before retry\n\t\treturn fmt.Errorf(\"cannot write %s: %w\", pathErr.Path, err)\n\t}\n\treturn err\n}","preventionTips":["Keep .beads on a local filesystem with atomic rename semantics","Ensure free disk space before large bd operations","Never mark metadata.json immutable or root-owned in shared workspaces","Serialize concurrent bd processes per workspace to reduce temp-file churn"],"tags":["filesystem","config","write","atomic-write","permissions"],"backgroundTag":"config-write-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}