gastownhall/beads · error
journal: snapshot comment for %s: %w
Error message
journal: snapshot comment for %s: %w
What it means
RecordCommentEventInTx wraps any failure from getJournalIssueInTx (fetching the issue snapshot plus its readiness projection) when recording a comment event in the bd_events_journal. The journal needs a full issue snapshot for every comment write, so if the snapshot lookup fails the whole comment transaction fails with this wrapped error.
Source
Thrown at internal/storage/issueops/journal.go:376
// the edge change with a null snapshot rather than failing.
if errors.Is(err, storage.ErrNotFound) {
return insertEventRow(ctx, tx, op, issueID, nil, &EventDep{Kind: kind, Target: target, Metadata: metadata}, nil, actor)
}
return fmt.Errorf("journal: snapshot %s for %s: %w", op, issueID, err)
}
return insertEventRow(ctx, tx, op, issueID, issue, &EventDep{Kind: kind, Target: target, Metadata: metadata}, nil, actor)
}
// RecordCommentEventInTx records a replayable structured or audit comment. The
// journal row's actor is the comment's Author — the identity that wrote it —
// so this takes no separate actor parameter.
func RecordCommentEventInTx(ctx context.Context, tx DBTX, issueID string, comment *EventComment) error {
if !journalEnabled(ctx, tx) {
return nil
}
issue, err := getJournalIssueInTx(ctx, tx, issueID)
if err != nil {
return fmt.Errorf("journal: snapshot comment for %s: %w", issueID, err)
}
return insertEventRow(ctx, tx, EventCommentWrite, issueID, issue, nil, comment, comment.Author)
}
// getJournalIssueInTx augments the normal issue snapshot with the persisted
// readiness projection. is_blocked is deliberately not part of ordinary issue
// hydration, but it is required in a journal snapshot because a dependency
// delta must be replayable without re-running graph maintenance.
func getJournalIssueInTx(ctx context.Context, tx DBTX, issueID string) (*types.Issue, error) {
for _, candidate := range []struct {
issueTable string
labelTable string
}{
{"issues", "labels"},
{"wisps", "wisp_labels"},
} {
issue, err := getIssueFromTableInTx(ctx, tx, candidate.issueTable, candidate.labelTable, issueID)
if errors.Is(err, storage.ErrNotFound) {View on GitHub (pinned to 71377f2769)
Solutions
- Verify the issue ID exists (bd show <id> or SELECT) before adding a comment
- Check whether the issue was deleted or renamed concurrently; re-fetch the ID
- Run bd doctor / schema checks to confirm issues and journal tables exist
- Inspect the wrapped %w error for the underlying SQL cause
Example fix
// before
err := ops.AddCommentEventInTx(ctx, tx, "bd-9999", comment) // id never existed
// after
var n int
_ = tx.QueryRowContext(ctx, "SELECT COUNT(*) FROM issues WHERE id = ?", id).Scan(&n)
if n == 0 { return fmt.Errorf("issue %s not found", id) }
err := ops.AddCommentEventInTx(ctx, tx, id, comment) Defensive patterns
Strategy: validation
Validate before calling
var n int
if err := tx.QueryRowContext(ctx, "SELECT COUNT(*) FROM issues WHERE id = ?", issueID).Scan(&n); err != nil { return err }
if n == 0 { return fmt.Errorf("issue %s does not exist", issueID) } Try / catch
if err := ops.RecordCommentEventInTx(ctx, tx, id, c); err != nil {
if errors.Is(err, storage.ErrNotFound) { /* skip or create issue */ }
return fmt.Errorf("comment journaling failed: %w", err)
} Prevention
- Resolve issue IDs through the store API, never free-form strings
- Check for concurrent deletes before commenting in sync jobs
- Keep schema migrated so the is_blocked probe succeeds
When it happens
Trigger: Calling AddCommentEventInTx, addIssueCommentInTx, or CreateIssueInTxWithResult for an issue ID that does not exist in the issues table (or in the tombstone/deleted-issues table checked by getJournalIssueInTx), or a DB error while reading the issue or its is_blocked column.
Common situations: Commenting on an issue that was deleted concurrently in another transaction; a typo'd or stale issue ID passed by an importer/sync tool; corrupted or missing journal schema so the is_blocked probe query fails.
Related errors
- %w: issue %s
- db: Update %s: %w
- db: Claim %s: read old issue: %w
- journal dependency removals for source-repo delete: %w
- capture dependency edges for rename %s -> %s: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/e84b6848a57343bf.
Report an issue: GitHub.