{"record":{"id":"b6bd05024ccf9a21","repo":"gastownhall/beads","slug":"journal-marshal-dep-for-s-w","errorCode":null,"errorMessage":"journal: marshal dep for %s: %w","messagePattern":"journal: marshal dep for (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/journal.go","lineNumber":433,"sourceCode":"// plumbings funnel through, so the seq mechanism cannot drift between them. A\n// nil issue is stored as SQL NULL (deletes); a nil dep is stored as SQL NULL\n// (non-dependency ops). ts is the insert time, stamped inside the committing\n// transaction. actor is stored as-is — \"\" for the genuinely unattributable\n// paths — in the NOT NULL DEFAULT ” actor column.\nfunc insertEventRow(ctx context.Context, tx DBTX, op EventOp, issueID string, issue *types.Issue, dep *EventDep, comment *EventComment, actor string) error {\n\tvar issueJSON any\n\tif issue != nil {\n\t\tb, err := json.Marshal(issue)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"journal: marshal issue %s: %w\", issueID, err)\n\t\t}\n\t\tissueJSON = string(b)\n\t}\n\tvar depJSON any\n\tif dep != nil {\n\t\tb, err := json.Marshal(dep)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"journal: marshal dep for %s: %w\", issueID, err)\n\t\t}\n\t\tdepJSON = string(b)\n\t}\n\tvar commentJSON any\n\tif comment != nil {\n\t\tb, err := json.Marshal(comment)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"journal: marshal comment for %s: %w\", issueID, err)\n\t\t}\n\t\tcommentJSON = string(b)\n\t}\n\tinsert := func(seq int64) error {\n\t\t_, err := tx.ExecContext(ctx, `\n\t\t\tINSERT INTO bd_events_journal (seq, ts, op, issue_id, actor, issue_json, dep_json, comment_json)\n\t\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?)\n\t\t`, seq, time.Now().UTC(), string(op), issueID, actor, issueJSON, depJSON, commentJSON)\n\t\treturn err\n\t}","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/journal.go#L415-L451","documentation":"insertEventRow marshals the EventDep payload to JSON for dependency journal rows; failure is wrapped as 'journal: marshal dep for %s'. EventDep is a small fixed struct so failures indicate corrupted dependency data or incompatible field types.","triggerScenarios":"RecordDepEventInTx / RecordEventInTx with a *EventDep containing invalid data (nil map edge cases after custom construction, wrong types from a hand-built event).","commonSituations":"Custom tooling constructing EventDep directly with non-standard values; merge tooling that pasted mismatched struct versions.","solutions":["Inspect the wrapped error for the offending field","Construct EventDep only via the library's dependency APIs","Ensure dependency payloads contain only plain strings/bools","Report upstream if standard API usage triggers this"],"exampleFix":"// before\ndep := &ops.EventDep{IssueID: id, Target: strings.Repeat(\"\\x00\", 10)}\nops.RecordDepEventInTx(ctx, tx, op, id, dep)\n// after\ndep := &ops.EventDep{IssueID: id, Target: targetID} // use valid IDs from the store\nops.RecordDepEventInTx(ctx, tx, op, id, dep)","handlingStrategy":"try-catch","validationCode":"if _, err := json.Marshal(dep); err != nil {\n    return fmt.Errorf(\"dep payload invalid: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := ops.RecordDepEventInTx(ctx, tx, op, id, dep); err != nil {\n    if strings.Contains(err.Error(), \"marshal dep\") { return fmt.Errorf(\"bad dep payload: %w\", err) }\n    return err\n}","preventionTips":["Build EventDep via library dependency APIs, not by hand","Keep dep fields as plain strings/bools","Round-trip test custom payloads through json.Marshal/Unmarshal"],"tags":["journal","json","serialization"],"backgroundTag":"json-marshal-failure","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}