{"record":{"id":"efb063b8649a6986","repo":"gastownhall/beads","slug":"failed-to-create-interactions-log-w","errorCode":null,"errorMessage":"failed to create interactions log: %w","messagePattern":"failed to create interactions log: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/audit/audit.go","lineNumber":103,"sourceCode":"\tp, err := Path()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif err := os.MkdirAll(filepath.Dir(p), 0700); err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to create .beads directory: %w\", err)\n\t}\n\tif ensureFileBeforeCreateHook != nil {\n\t\tensureFileBeforeCreateHook(p)\n\t}\n\tf, err := os.OpenFile(p, os.O_CREATE|os.O_EXCL|os.O_WRONLY, 0644) // nolint:gosec // JSONL is intended to be shared via git across clones/tools.\n\tif err == nil {\n\t\tif closeErr := f.Close(); closeErr != nil {\n\t\t\treturn \"\", fmt.Errorf(\"failed to close interactions log: %w\", closeErr)\n\t\t}\n\t\treturn p, nil\n\t}\n\tif !errors.Is(err, os.ErrExist) {\n\t\treturn \"\", fmt.Errorf(\"failed to create interactions log: %w\", err)\n\t}\n\treturn p, nil\n}\n\n// Append appends an event to .beads/interactions.jsonl as a single JSON line.\n// This is intentionally append-only: callers must not mutate existing lines.\nfunc Append(e *Entry) (string, error) {\n\tif e == nil {\n\t\treturn \"\", fmt.Errorf(\"nil entry\")\n\t}\n\tif e.Kind == \"\" {\n\t\treturn \"\", fmt.Errorf(\"kind is required\")\n\t}\n\n\tp, err := EnsureFile()\n\tif err != nil {\n\t\treturn \"\", err\n\t}","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/audit/audit.go#L85-L121","documentation":"EnsureFile creates .beads/interactions.jsonl and returns an error wrapped as \"failed to create interactions log\" when os.OpenFile with O_CREATE|O_EXCL fails with an error other than os.ErrExist. This guards the create-only race in the audit sidecar: a concurrent creator winning the O_EXCL race is treated as success, any other open failure is surfaced here. The error is always a wrapped underlying OS error.","triggerScenarios":"Calling Append or AppendIfEnabled (which call EnsureFile) when Path() fails, os.MkdirAll of .beads fails (wrapped differently), or OpenFile(O_EXCL) fails with permission denied, a read-only filesystem, or a non-ErrExist error like EACCES/ENOTDIR on the interactions.jsonl path.","commonSituations":"Running bd in a repo checkout where .beads/ or the file is owned by another user; CI containers with read-only workspaces; BEADS_DIR pointing into a directory the current user cannot write; antivirus or filesystem restrictions blocking creation.","solutions":["Fix filesystem permissions on the .beads directory so the current user can create files (chmod/chown).","Check that the target path is on a writable filesystem, not a read-only mount or read-only container layer.","Verify BEADS_DIR points to an existing writable directory.","Inspect the wrapped %w cause with errors.Is/errors.As to identify the exact OS error.","If you need audit logging, run bd from a checkout that has (or can create) a .beads directory."],"exampleFix":"// before\np, err := audit.AppendIfEnabled(entry) // fails: permission denied in shared clone\n// after\nif err := os.Chmod(\".beads\", 0o700); err != nil { log.Fatal(err) } // or chown the checkout\np, err := audit.AppendIfEnabled(entry)","handlingStrategy":"try-catch","validationCode":"if _, statErr := os.Stat(\".beads\"); statErr != nil && os.IsPermission(statErr) {\n    return fmt.Errorf(\"cannot write .beads: %w\", statErr)\n}","typeGuard":null,"tryCatchPattern":"if _, err := audit.AppendIfEnabled(entry); err != nil {\n    if errors.Is(err, fs.ErrPermission) || errors.Is(err, syscall.EROFS) {\n        log.Printf(\"audit log not writable: %v\", err)\n        return nil // degrade gracefully\n    }\n    return err\n}","preventionTips":["Run bd from a checkout you own and can write to.","Keep .beads/ on a writable filesystem, not a read-only mount.","Check BEADS_DIR before running in shared environments.","Unwrap errors with errors.Is to distinguish permission vs existence problems."],"tags":["go","filesystem","audit-log","permissions"],"backgroundTag":"file-create-permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}