gastownhall/beads · error
db: CommentSQLRepository.Insert: %w
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.
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)
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
- failed to begin transaction: %w
- failed to recompute is_blocked: %w
- failed to commit is_blocked repairs: %w
- failed to query orphaned dependencies: %w
- row iteration error: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/da90f583f899a76c.
Report an issue: GitHub.