gastownhall/beads · error
db: CommentSQLRepository.Insert
Error message
db: CommentSQLRepository.Insert: %w
What it means
Insert first calls issueops.NextLiveCommentTime to compute a created_at stamp that is strictly newer than the issue's newest comment, guaranteeing in-order reads within the same second. This error wraps any failure from that timestamp computation (which itself queries the comment table). The comment is not inserted when this fires.
Solutions
- Run migrations so the selected comment table exists; check dberrors.IsTableNotExist on the wrapped cause.
- Verify UseWispsTable matches the table your issue lives in.
- Inspect the wrapped error for cancellation/timeouts and raise the context deadline.
- If using InsertRecord with an explicit CreatedAt, this path is bypassed — precompute a stamp yourself when appropriate.
Example fix
// before: inserting into wisp_comments on a DB without wisp schema
repo.Insert(ctx, issueID, "alice", "hello", domain.CommentOpts{UseWispsTable: true})
// after: ensure wisp schema exists (or flip UseWispsTable) before inserting
if err := ensureWispSchema(ctx, conn); err != nil { return err }
c, err := repo.Insert(ctx, issueID, "alice", "hello", opts) Defensive patterns
Strategy: validation
Validate before calling
table := "comments"
if opts.UseWispsTable { table = "wisp_comments" }
var exists int
err := conn.QueryRowContext(ctx,
"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?",
table).Scan(&exists)
if err != nil || exists == 0 { return fmt.Errorf("cannot insert comment: %s missing", table) } Type guard
func isInsertStampError(err error) bool {
return err != nil && strings.Contains(err.Error(), "CommentSQLRepository.Insert:")
} Try / catch
c, err := repo.Insert(ctx, issueID, author, text, opts)
if err != nil && isInsertStampError(err) {
if dberrors.IsTableNotExist(err) { migrate(); c, err = repo.Insert(ctx, issueID, author, text, opts) }
if err != nil { return nil, err }
} Prevention
- Migrate comment tables before writes
- Keep UseWispsTable consistent with issue storage
- Avoid canceling contexts mid-insert; use InsertRecord when you control timestamps
When it happens
Trigger: Calling CommentSQLRepository.Insert(ctx, issueID, author, text, opts) when NextLiveCommentTime cannot read the latest comment time — e.g. the comment table (`comments`/`wisp_comments`) is missing, the query errors, or the context is canceled.
Common situations: Inserting comments into a database whose comment table wasn't migrated; UseWispsTable set incorrectly so the wrong table is probed; concurrent insert storms making the timestamp query contend.
Related errors
- add federation peer
- add label
- clear conflict
- db: ChildCounterSQLRepository.NextChildID: rows
- db: ChildCounterSQLRepository.NextChildID: scan
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/da90f583f899a76c.
Report an issue: GitHub.
Appendix: source
Thrown at internal/storage/domain/db/comment.go:123
return result, nil
}
func (r *commentSQLRepositoryImpl) IterByIssueID(ctx context.Context, issueID string, opts domain.CommentOpts) (storage.Iter[types.Comment], error) {
bulk, err := r.ListByIssueIDs(ctx, []string{issueID}, opts)
if err != nil {
return nil, err
}
return storage.NewSliceIter(bulk[issueID]), nil
}
func (r *commentSQLRepositoryImpl) Insert(ctx context.Context, issueID, author, text string, opts domain.CommentOpts) (*types.Comment, error) {
// Live add: advance past the issue's newest comment so a burst inside one
// second still reads back in write order (issueops.NextLiveCommentTime).
// InsertRecord honors a supplied CreatedAt verbatim, which is what keeps
// imported comments on their original timestamps.
stamp, err := issueops.NextLiveCommentTime(ctx, r.runner, pickCommentTable(opts.UseWispsTable), issueID, time.Now())
if err != nil {
return nil, fmt.Errorf("db: CommentSQLRepository.Insert: %w", err)
}
return r.InsertRecord(ctx, &types.Comment{IssueID: issueID, Author: author, Text: text, CreatedAt: stamp}, opts)
}
func (r *commentSQLRepositoryImpl) InsertRecord(ctx context.Context, comment *types.Comment, opts domain.CommentOpts) (*types.Comment, error) {
if comment == nil {
return nil, fmt.Errorf("db: CommentSQLRepository.InsertRecord: comment must not be nil")
}
copy := *comment
if copy.IssueID == "" {
return nil, fmt.Errorf("db: CommentSQLRepository.InsertRecord: issueID must not be empty")
}
issueTable := pickIssueTable(opts.UseWispsTable)
var exists bool
//nolint:gosec // G201: issueTable is one of two hardcoded constants
if err := r.runner.QueryRowContext(ctx,
fmt.Sprintf("SELECT EXISTS(SELECT 1 FROM %s WHERE id = ?)", issueTable), copy.IssueID).Scan(&exists); err != nil {View on GitHub (pinned to 71377f2769)