gastownhall/beads · error

failed to insert comment for %s: %w

Error message

failed to insert comment for %s: %w

What it means

PersistComments stamps each structured comment with a timestamp via NextLiveCommentTime so comments created in the same second as the issue's newest comment still order deterministically. If querying/advancing that timestamp fails (a database error from the underlying time query), the comment insert is aborted and the error is wrapped with the issue ID. The whole create transaction's comment persistence stops at that point.

Source

Thrown at internal/storage/issueops/create.go:800

	var result CreateIssueResult
	if len(issue.Comments) == 0 {
		return result, nil
	}
	commentTable := "comments"
	if IsWisp(issue) {
		commentTable = "wisp_comments"
	}
	for _, comment := range issue.Comments {
		createdAt := comment.CreatedAt
		if createdAt.IsZero() {
			// No supplied timestamp: this is a live comment, so stamp it the
			// same way AddIssueComment does — one second past the issue's
			// newest comment when the clock second would collide. Otherwise
			// several such comments in one create share a second and read back
			// in content-digest order rather than the order they were listed.
			stamped, err := NextLiveCommentTime(ctx, tx, commentTable, issue.ID, time.Now())
			if err != nil {
				return result, fmt.Errorf("failed to insert comment for %s: %w", issue.ID, err)
			}
			createdAt = stamped
		}
		createdAtText := FormatAuxTime(createdAt)
		if comment.ID == "" {
			// No incoming id (fresh comment): content-derived id, collapsing
			// onto an identical existing row exactly like the import dedup.
			id, existed, err := InsertDerivedComment(ctx, tx, commentTable, issue.ID, comment.Author, comment.Text, createdAtText)
			if err != nil {
				return result, fmt.Errorf("failed to insert comment for %s: %w", issue.ID, err)
			}
			comment.ID = id
			if !existed {
				result.markChanged(commentTable)
				result.persistedComments = append(result.persistedComments, EventComment{
					ID: id, Author: comment.Author, Text: comment.Text, CreatedAt: createdAt, Source: CommentSourceStructured,
				})
			}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped error (%w) for the underlying DB failure (locked DB, no such table, ctx deadline) and fix that root cause.
  2. Retry the create once the database is reachable/locks are released.
  3. Verify the comment table schema matches the expected migration version (run the repo's schema migration/doctor).
  4. Increase the context timeout when creating issues with many comments in one transaction.

Example fix

// before
ctx := context.Background() // no deadline control; large create times out
result, err := issueops.CreateIssueInTxWithResult(ctx, tx, issue, opts)
// after
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
result, err := issueops.CreateIssueInTxWithResult(ctx, tx, issue, opts)
Defensive patterns

Strategy: retry

Validate before calling

// verify DB reachable and comment table present before create
if err := tx.PingContext(ctx); err != nil { return err }
var n int
if err := tx.QueryRowContext(ctx,
    "SELECT COUNT(*) FROM sqlite_master WHERE name = 'issue_comments'").Scan(&n); err != nil || n == 0 {
    return fmt.Errorf("comment table missing; run migrations")
}

Try / catch

result, err := issueops.CreateIssueInTxWithResult(ctx, tx, issue, opts)
if err != nil && strings.Contains(err.Error(), "failed to insert comment") {
    if isTransient(err) { // locked / connection reset / deadline
        return retryWithBackoff(ctx, func() error {
            _, e := issueops.CreateIssueInTxWithResult(ctx, tx, issue, opts)
            return e
        })
    }
    return err
}

Prevention

When it happens

Trigger: Creating an issue with structured comments via CreateIssueInTxWithResult when NextLiveCommentTime's query against the comment table fails — e.g. the comment table is missing/corrupt, the connection dropped mid-transaction, or the context was cancelled.

Common situations: Database locked or unavailable during bulk import; schema migration mismatch leaving an old comment table; ctx timeout expiring during a large multi-comment create; disk-full or I/O errors on the embedded Dolt database.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/18d9cdf59375b03b. Report an issue: GitHub.