{"record":{"id":"21565a0b52647ede","repo":"gastownhall/beads","slug":"failed-to-check-comment-existence-for-s-w","errorCode":null,"errorMessage":"failed to check comment existence for %s: %w","messagePattern":"failed to check comment existence for (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/create.go","lineNumber":829,"sourceCode":"\t\t\t}\n\t\t\tcomment.ID = id\n\t\t\tif !existed {\n\t\t\t\tresult.markChanged(commentTable)\n\t\t\t\tresult.persistedComments = append(result.persistedComments, EventComment{\n\t\t\t\t\tID: id, Author: comment.Author, Text: comment.Text, CreatedAt: createdAt, Source: CommentSourceStructured,\n\t\t\t\t})\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\t// Incoming id (import/interchange): preserve it, with the historical\n\t\t// existence check preventing duplicates on re-import.\n\t\tvar exists int\n\t\t//nolint:gosec // G201: table is determined by ephemeral flag\n\t\tif err := tx.QueryRowContext(ctx, fmt.Sprintf(`\n\t\t\t\tSELECT COUNT(*) FROM %s\n\t\t\t\tWHERE issue_id = ? AND author = ? AND created_at = ? AND text = ?\n\t\t\t`, commentTable), issue.ID, comment.Author, createdAtText, comment.Text).Scan(&exists); err != nil {\n\t\t\treturn result, fmt.Errorf(\"failed to check comment existence for %s: %w\", issue.ID, err)\n\t\t}\n\t\tif exists > 0 {\n\t\t\tcontinue\n\t\t}\n\t\t//nolint:gosec // G201: table is determined by ephemeral flag\n\t\t_, err := tx.ExecContext(ctx, fmt.Sprintf(`\n\t\t\tINSERT INTO %s (id, issue_id, author, text, created_at)\n\t\t\tVALUES (?, ?, ?, ?, ?)\n\t\t`, commentTable), comment.ID, issue.ID, comment.Author, comment.Text, createdAtText)\n\t\tif err != nil {\n\t\t\treturn result, fmt.Errorf(\"failed to insert comment for %s: %w\", issue.ID, err)\n\t\t}\n\t\tresult.markChanged(commentTable)\n\t\tresult.persistedComments = append(result.persistedComments, EventComment{\n\t\t\tID: comment.ID, Author: comment.Author, Text: comment.Text, CreatedAt: createdAt, Source: CommentSourceStructured,\n\t\t})\n\t}\n\treturn result, nil","sourceCodeStart":811,"sourceCodeEnd":847,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/create.go#L811-L847","documentation":"For comments that carry an explicit ID, PersistComments first runs a COUNT query to check whether an identical comment row (same issue_id, author, created_at, text) already exists; if the query itself fails, the error is wrapped as 'failed to check comment existence'. This is a read-failure path, not a duplicate-detection error — duplicates found this way are silently skipped (continue).","triggerScenarios":"Creating an issue with pre-ID'd comments via CreateIssueInTxWithResult when the COUNT(*) SELECT against the comment table errors — wrong table name after schema drift, DB connection loss, cancelled context, or a corrupted comment table.","commonSituations":"Remote DB connection dropping mid-import; context deadline exceeded on large transactions; manual schema edits breaking the comments table; running against an uninitialized database lacking the comment table.","solutions":["Check the wrapped %w error for the SQL failure mode and fix the root cause (connection, schema, timeout).","Run schema initialization/migrations so the comment table exists with the expected columns.","Retry with a longer-lived context; keep transactions small enough to finish within deadlines.","Verify DB health (bd doctor / driver connectivity) before bulk create operations."],"exampleFix":"// before\nctx, _ := context.WithTimeout(context.Background(), 2*time.Second) // too short for large import\nresult, err := issueops.CreateIssueInTxWithResult(ctx, tx, issue, opts)\n// after\nctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)\ndefer cancel()\nresult, err := issueops.CreateIssueInTxWithResult(ctx, tx, issue, opts)","handlingStrategy":"validation","validationCode":"var n int\nerr := tx.QueryRowContext(ctx,\n    \"SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=?\", commentTable).Scan(&n)\nif err != nil || n == 0 {\n    return fmt.Errorf(\"comment table %s missing; run migrations first\", commentTable)\n}","typeGuard":null,"tryCatchPattern":"result, err := issueops.CreateIssueInTxWithResult(ctx, tx, issue, opts)\nif err != nil && strings.Contains(err.Error(), \"failed to check comment existence\") {\n    if ctx.Err() != nil { return ctx.Err() } // deadline/cancel — rerun with bigger budget\n    return fmt.Errorf(\"comment table unreadable: %w\", err)\n}","preventionTips":["Initialize/migrate the schema before any create operations.","Use context timeouts sized to the batch, not fixed tiny values.","Verify DB connectivity before long import runs.","Avoid manual edits to the comments table schema."],"tags":["storage","comments","database"],"backgroundTag":"comment-persistence-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}