{"record":{"id":"b98ad2e250dc8a65","repo":"gastownhall/beads","slug":"cannot-write-default-nix-v","errorCode":null,"errorMessage":"cannot write default.nix: %v","messagePattern":"cannot write default\\.nix: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/preflight.go","lineNumber":774,"sourceCode":"\t}\n\tnixPerm := nixInfo.Mode().Perm()\n\n\tcontent, err := os.ReadFile(nixPath)\n\tif err != nil {\n\t\treturn false, \"\", \"\", fmt.Errorf(\"cannot read default.nix: %v\", err)\n\t}\n\n\tre := regexp.MustCompile(`(vendorHash\\s*=\\s*)\"([^\"]+)\"`)\n\tloc := re.FindSubmatchIndex(content)\n\tif loc == nil {\n\t\treturn false, \"\", \"\", fmt.Errorf(\"vendorHash not found in default.nix\")\n\t}\n\toldHash := string(content[loc[4]:loc[5]])\n\n\tconst sentinel = \"sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\"\n\tprobed := append(append([]byte{}, content[:loc[4]]...), append([]byte(sentinel), content[loc[5]:]...)...)\n\tif err := os.WriteFile(nixPath, probed, nixPerm); err != nil {\n\t\treturn false, \"\", \"\", fmt.Errorf(\"cannot write default.nix: %v\", err)\n\t}\n\n\trestored := false\n\tdefer func() {\n\t\tif !restored {\n\t\t\t_ = os.WriteFile(nixPath, content, nixPerm)\n\t\t}\n\t}()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)\n\tdefer cancel()\n\tnixCmd := exec.CommandContext(ctx, \"nix\", \"build\", \".#default\", \"--no-link\")\n\tnixOut, _ := nixCmd.CombinedOutput()\n\n\t// Nix prints the correct hash in lines like \"got:    sha256-...\"\n\thashRe := regexp.MustCompile(`got:\\s+(sha256-[A-Za-z0-9+/]+=)`)\n\tm := hashRe.FindSubmatch(nixOut)\n\tif m == nil {","sourceCodeStart":756,"sourceCodeEnd":792,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/preflight.go#L756-L792","documentation":"To discover the correct hash, fixNixHash writes a sentinel value (sha256-AAAA...) into default.nix and runs `nix build`, relying on Nix's mismatch error to reveal the real hash. If the probe write with os.WriteFile fails (permissions, read-only filesystem, disk full), this error is returned; a deferred restore also rewrites the original contents.","triggerScenarios":"os.WriteFile(\"default.nix\", probed, perm) fails during `bd fix` — directory not writable by the current user, filesystem mounted read-only, or insufficient disk space/inodes.","commonSituations":"Running `bd fix` inside a read-only CI checkout (common with checkout actions that set read-only for cache keys); repo owned by root while bd runs as a normal user; immutable files (chattr +i).","solutions":["Make the file writable: `chmod u+w default.nix` or take ownership `sudo chown $USER default.nix`","Re-run in a writable checkout (clone fresh or unset the read-only flag on the workspace)","If the filesystem is read-only, run `bd fix` from a writable copy of the repo"],"exampleFix":"// before\n$ ls -l default.nix  ->  -r--r--r--\ncannot write default.nix: open default.nix: permission denied\n// after\nchmod u+w default.nix && bd fix","handlingStrategy":"validation","validationCode":"[ -w default.nix ] || { echo \"default.nix must be writable to auto-fix hash\"; exit 1; }","typeGuard":null,"tryCatchPattern":"out, err := exec.Command(\"bd\", \"fix\").CombinedOutput()\nif err != nil && strings.Contains(string(out), \"cannot write default.nix\") {\n    // chmod u+w / use a writable checkout, then retry\n}","preventionTips":["Run bd fix in a writable clone, not a read-only CI checkout","Keep the repo owned by the user running bd fix","Avoid immutable flags (chattr +i) on generated nix files"],"tags":["nix","filesystem","permissions","file-write"],"backgroundTag":"file-write-permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}