{"record":{"id":"cac0c517f3b8300e","repo":"gastownhall/beads","slug":"atomicfile-chmod-w","errorCode":null,"errorMessage":"atomicfile: chmod: %w","messagePattern":"atomicfile: chmod: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/atomicfile/atomicfile.go","lineNumber":79,"sourceCode":"// Write delegates to the underlying temp file.\nfunc (w *Writer) Write(p []byte) (int, error) {\n\treturn w.f.Write(p)\n}\n\n// Close fsyncs the temp file and atomically renames it to the target path.\n// After Close returns successfully, the target contains exactly the data\n// written. On error the temp file is removed and the target is untouched.\nfunc (w *Writer) Close() error {\n\tif w.done {\n\t\treturn nil\n\t}\n\tw.done = true\n\n\t// Ensure permissions before rename — CreateTemp uses 0600 by default.\n\tif err := w.f.Chmod(w.perm); err != nil {\n\t\t_ = w.f.Close()\n\t\t_ = os.Remove(w.f.Name())\n\t\treturn fmt.Errorf(\"atomicfile: chmod: %w\", err)\n\t}\n\n\tif err := w.f.Sync(); err != nil {\n\t\t_ = w.f.Close()\n\t\t_ = os.Remove(w.f.Name())\n\t\treturn fmt.Errorf(\"atomicfile: sync: %w\", err)\n\t}\n\n\tif err := w.f.Close(); err != nil {\n\t\t_ = os.Remove(w.f.Name())\n\t\treturn fmt.Errorf(\"atomicfile: close: %w\", err)\n\t}\n\n\tif err := os.Rename(w.f.Name(), w.target); err != nil {\n\t\t_ = os.Remove(w.f.Name())\n\t\treturn fmt.Errorf(\"atomicfile: rename: %w\", err)\n\t}\n","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/atomicfile/atomicfile.go#L61-L97","documentation":"Before renaming the temp file into place, atomicfile.Writer.Close calls f.Chmod(w.perm) because os.CreateTemp creates files with 0600. If the chmod syscall fails, Close removes the temp file and returns this wrapped error, leaving the target untouched.","triggerScenarios":"Closing a Writer whose file descriptor has become invalid (already closed elsewhere), or a filesystem/OS refusing the chmod (e.g. some FUSE/network mounts, or the file was deleted under the process).","commonSituations":"Writing atomic files on exotic mounts (NFS, some container volumes) that don't support chmod, or double-Close misuse of the Writer.","solutions":["Retry the write on a filesystem that supports permission changes (local ext4/APFS/NTFS rather than the problematic mount)","Ensure Close is called exactly once per Writer to avoid operating on a closed fd","If perm doesn't matter on your target, keep the default 0600 path but verify mount support"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := w.Close(); err != nil {\n    if strings.Contains(err.Error(), \"atomicfile: chmod\") {\n        log.Printf(\"target fs may not support chmod: %v\", err)\n        // fall back to plain os.WriteFile or different mount\n    }\n    return err\n}","preventionTips":["Write atomic files to local filesystems, not FUSE/NFS mounts without chmod support","Call Close exactly once; use Abort for the discard path","Test your deployment filesystem supports Chmod before relying on atomic writes"],"tags":["filesystem","permissions","atomic-write"],"backgroundTag":"file-chmod-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}