gastownhall/beads · error

check comment existence in %s: %w

Error message

check comment existence in %s: %w

What it means

InsertDerivedComment first probes for an existing identical comment row (content-derived dedup); this wraps any probe error other than sql.ErrNoRows. It means the existence-check SELECT (issue_id, author, text, created_at with NULL-safe comparison, ORDER BY id LIMIT 1) failed at the SQL level — schema mismatch, connection failure, or cancelled context.

Source

Thrown at internal/storage/issueops/derivedid.go:225

// InsertDerivedComment inserts a comment under its content-derived id, or
// collapses onto an existing identical comment: a same-content row already in
// table (any id — it may predate the derivation) is the same logical comment,
// and the import path has always existence-checked exactly this column set
// (issue_id, author, text, created_at) rather than insert a duplicate. It
// returns the surviving row's id and whether it already existed.
//
//nolint:gosec // G201: table is a hardcoded routing constant at every call site.
func InsertDerivedComment(ctx context.Context, tx DBTX, table, issueID, author, text, createdAt string) (id string, existed bool, err error) {
	err = tx.QueryRowContext(ctx, fmt.Sprintf(`
		SELECT id FROM %s
		WHERE issue_id = ? AND author = ? AND text = ? AND created_at = ?
		ORDER BY id LIMIT 1`, table),
		issueID, author, text, createdAt).Scan(&id)
	if err == nil {
		return id, true, nil
	}
	if err != sql.ErrNoRows {
		return "", false, fmt.Errorf("check comment existence in %s: %w", table, err)
	}
	digest := rowid.Digest([]sql.NullString{str(issueID), str(author), str(text), str(createdAt)})
	id = rowid.New(table, 0, digest)
	if _, err := tx.ExecContext(ctx, fmt.Sprintf(`
		INSERT INTO %s (id, issue_id, author, text, created_at)
		VALUES (?, ?, ?, ?, ?)`, table),
		id, issueID, author, text, createdAt); err != nil {
		return "", false, fmt.Errorf("add comment to %s: %w", table, err)
	}
	return id, false, nil
}

// InsertDerivedCompactionSnapshot inserts a compaction_snapshots row under
// its content-derived id, with the same ordinal discipline as events. Two
// clones compacting the same issue at the same tier in the same second
// produce byte-identical snapshots and therefore the same id.
func InsertDerivedCompactionSnapshot(ctx context.Context, tx DBTX, issueID string, level int, snapshotJSON []byte, createdAt string) error {
	if createdAt == "" {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Apply schema migrations so the comments table matches the expected shape.
  2. Confirm the table constant routes to an existing derived comments table.
  3. Retry in a fresh transaction if the connection was aborted.
  4. Read the wrapped driver error (`errors.Unwrap` chain) for the precise SQL message.

Example fix

// before
if err != sql.ErrNoRows {
	return "", false, fmt.Errorf("check comment existence in %s: %w", table, err)
}
// after: also handle context cancellation distinctly
if err != sql.ErrNoRows {
	if ctx.Err() != nil { return "", false, ctx.Err() }
	return "", false, fmt.Errorf("check comment existence in %s: %w", table, err)
}
Defensive patterns

Strategy: try-catch

Validate before calling

if err := ctx.Err(); err != nil { return err }
cols, err := tableColumns(tx, table)
if err != nil { return err }
for _, c := range []string{"id", "issue_id", "author", "text", "created_at"} {
	if !slices.Contains(cols, c) {
		return fmt.Errorf("%s missing %s; migrate first", table, c)
	}
}

Type guard

func isNonNoRowsQueryErr(err error) bool {
	return err != nil && !errors.Is(err, sql.ErrNoRows)
}

Try / catch

id, created, err := InsertDerivedComment(ctx, tx, table, issueID, author, text, createdAt)
if err != nil {
	if errors.Is(err, context.Canceled) { return err }
	if strings.Contains(err.Error(), "Unknown column") {
		return fmt.Errorf("migrate comment schema: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: addIssueCommentInTx / PersistComments run against a comments table missing expected columns, a wrong table constant, or with a broken connection inside the transaction.

Common situations: Databases created before comment-table migrations; hand-edited schemas; Dolt connection dropped mid-transaction during import (PersistComments).

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


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