gastownhall/beads · error
journal: marshal dep for %s: %w
Error message
journal: marshal dep for %s: %w
What it means
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.
Source
Thrown at internal/storage/issueops/journal.go:433
// plumbings funnel through, so the seq mechanism cannot drift between them. A
// nil issue is stored as SQL NULL (deletes); a nil dep is stored as SQL NULL
// (non-dependency ops). ts is the insert time, stamped inside the committing
// transaction. actor is stored as-is — "" for the genuinely unattributable
// paths — in the NOT NULL DEFAULT ” actor column.
func insertEventRow(ctx context.Context, tx DBTX, op EventOp, issueID string, issue *types.Issue, dep *EventDep, comment *EventComment, actor string) error {
var issueJSON any
if issue != nil {
b, err := json.Marshal(issue)
if err != nil {
return fmt.Errorf("journal: marshal issue %s: %w", issueID, err)
}
issueJSON = string(b)
}
var depJSON any
if dep != nil {
b, err := json.Marshal(dep)
if err != nil {
return fmt.Errorf("journal: marshal dep for %s: %w", issueID, err)
}
depJSON = string(b)
}
var commentJSON any
if comment != nil {
b, err := json.Marshal(comment)
if err != nil {
return fmt.Errorf("journal: marshal comment for %s: %w", issueID, err)
}
commentJSON = string(b)
}
insert := func(seq int64) error {
_, err := tx.ExecContext(ctx, `
INSERT INTO bd_events_journal (seq, ts, op, issue_id, actor, issue_json, dep_json, comment_json)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
`, seq, time.Now().UTC(), string(op), issueID, actor, issueJSON, depJSON, commentJSON)
return err
}View on GitHub (pinned to 71377f2769)
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
Example fix
// before
dep := &ops.EventDep{IssueID: id, Target: strings.Repeat("\x00", 10)}
ops.RecordDepEventInTx(ctx, tx, op, id, dep)
// after
dep := &ops.EventDep{IssueID: id, Target: targetID} // use valid IDs from the store
ops.RecordDepEventInTx(ctx, tx, op, id, dep) Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := json.Marshal(dep); err != nil {
return fmt.Errorf("dep payload invalid: %w", err)
} Try / catch
if err := ops.RecordDepEventInTx(ctx, tx, op, id, dep); err != nil {
if strings.Contains(err.Error(), "marshal dep") { return fmt.Errorf("bad dep payload: %w", err) }
return err
} Prevention
- 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
When it happens
Trigger: RecordDepEventInTx / RecordEventInTx with a *EventDep containing invalid data (nil map edge cases after custom construction, wrong types from a hand-built event).
Common situations: Custom tooling constructing EventDep directly with non-standard values; merge tooling that pasted mismatched struct versions.
Related errors
- journal: marshal issue %s: %w
- journal: marshal comment for %s: %w
- failed to marshal backup state: %w
- failed to marshal issue %s: %w
- failed to write JSON: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/b6bd05024ccf9a21.
Report an issue: GitHub.