{"record":{"id":"a826459784c75961","repo":"gastownhall/beads","slug":"nil-entry","errorCode":null,"errorMessage":"nil entry","messagePattern":"nil entry","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/audit/audit.go","lineNumber":112,"sourceCode":"\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}\n\n\tif e.ID == \"\" {\n\t\te.ID, err = newID()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\tif e.CreatedAt.IsZero() {\n\t\te.CreatedAt = time.Now().UTC()","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/audit/audit.go#L94-L130","documentation":"Append validates its *Entry argument before touching the audit log and returns this plain error when the caller passes a nil pointer. It is a programmer-error guard, not an environmental failure — the library never constructs nil entries itself. The error message is the literal string \"nil entry\" with no wrapping.","triggerScenarios":"Calling audit.Append(nil) directly, or AppendIfEnabled(nil), or a variable holding *audit.Entry that was never initialized (e.g. a function returning (nil, err) whose err was ignored before appending).","commonSituations":"Constructing entries conditionally and forgetting a branch; passing the result of another constructor that returned nil on failure; refactors changing signatures from value to pointer types.","solutions":["Pass a non-nil *audit.Entry with at least Kind set.","Check the pointer for nil before calling Append.","If the entry comes from another function, check that function's error before using its result.","Set BD_AUDIT_ENABLED/audit.enabled and retry only after fixing the caller; this error is not environment-related."],"exampleFix":"// before\naudit.Append(entry) // panics-free but returns \"nil entry\" because entry == nil\n// after\nif entry == nil {\n    return fmt.Errorf(\"no audit entry to record\")\n}\nif _, err := audit.Append(entry); err != nil {\n    return fmt.Errorf(\"audit: %w\", err)\n}","handlingStrategy":"validation","validationCode":"if entry == nil {\n    return fmt.Errorf(\"refusing to append: audit entry is nil\")\n}","typeGuard":"func validEntry(e *audit.Entry) bool { return e != nil }","tryCatchPattern":"if err != nil && err.Error() == \"nil entry\" {\n    return fmt.Errorf(\"caller bug: constructed nil audit entry\")\n}","preventionTips":["Never pass a possibly-nil *Entry to Append; nil-check at construction sites.","Check errors from any function that returns (*audit.Entry, error) before using the pointer.","Construct entries in a single helper so nil cannot leak through."],"tags":["go","nil-pointer","validation","audit"],"backgroundTag":"nil-argument","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}