{"record":{"id":"2ca3adaaf829d257","repo":"gastownhall/beads","slug":"scan-same-content-events-in-s-w","errorCode":null,"errorMessage":"scan same-content events in %s: %w","messagePattern":"scan same-content events in (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/issueops/derivedid.go","lineNumber":137,"sourceCode":"\t\tfor _, p := range []*sql.NullString{&e.OldValue, &e.NewValue, &e.Comment} {\n\t\t\tif !p.Valid {\n\t\t\t\t*p = str(\"\")\n\t\t\t}\n\t\t}\n\t}\n\tdigest := rowid.Digest([]sql.NullString{\n\t\tstr(e.IssueID), str(string(e.EventType)), str(e.Actor),\n\t\te.OldValue, e.NewValue, e.Comment, str(e.CreatedAt),\n\t})\n\ttaken := make(map[string]bool)\n\trows, err := tx.QueryContext(ctx, fmt.Sprintf(`\n\t\tSELECT id FROM %s\n\t\tWHERE issue_id = ? AND event_type = ? AND actor = ?\n\t\t  AND old_value <=> ? AND new_value <=> ? AND comment <=> ?\n\t\t  AND created_at = ?`, table),\n\t\te.IssueID, string(e.EventType), e.Actor, e.OldValue, e.NewValue, e.Comment, e.CreatedAt)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"scan same-content events in %s: %w\", table, err)\n\t}\n\tfor rows.Next() {\n\t\tvar id string\n\t\tif err := rows.Scan(&id); err != nil {\n\t\t\t_ = rows.Close()\n\t\t\treturn \"\", fmt.Errorf(\"scan same-content events in %s: %w\", table, err)\n\t\t}\n\t\ttaken[id] = true\n\t}\n\t_ = rows.Close()\n\tif err := rows.Err(); err != nil {\n\t\treturn \"\", fmt.Errorf(\"scan same-content events in %s: %w\", table, err)\n\t}\n\n\tid := firstFreeDerivedID(table, digest, taken)\n\tif _, err := tx.ExecContext(ctx, fmt.Sprintf(`\n\t\tINSERT INTO %s (id, issue_id, event_type, actor, old_value, new_value, comment, created_at)\n\t\tVALUES (?, ?, ?, ?, ?, ?, ?, ?)`, table),","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/issueops/derivedid.go#L119-L155","documentation":"In InsertDerivedEventReturningID, a dedup lookup selects ids of existing same-content events; this wraps the QueryContext failure itself (before any rows are read). The query uses `<=>` (NULL-safe equality) against the derived events table routed by `table`; any SQL-level error — bad table name, missing column, connection failure — surfaces here.","triggerScenarios":"Calling AddCommentEventInTx or InsertDerivedEvent against a table missing the expected columns (id, issue_id, event_type, actor, old_value, new_value, comment, created_at), or with a dead/cancelled connection or wrong `table` routing constant.","commonSituations":"Older databases lacking newer event-table columns; passing an unsupported table constant; Dolt connection dropped inside a long transaction.","solutions":["Run schema migrations so the events table has all expected columns.","Verify the table routing constant matches an existing derived events table.","Check tx health / connection state; retry outside the failed transaction if it's aborted.","Inspect the wrapped driver error (`%w` chain) for the precise SQL failure (e.g. 'Unknown column')."],"exampleFix":"// before: column missing on old schema\n_, err := tx.QueryContext(ctx, fmt.Sprintf(`SELECT id FROM %s WHERE ... AND comment <=> ?`, table), ...)\n// after: migrate first, e.g. `bd migrate` / ensure ALTER TABLE applied before use\nif err := migrate(ctx, db); err != nil { return err }\n_, err := tx.QueryContext(ctx, fmt.Sprintf(`SELECT id FROM %s WHERE ... AND comment <=> ?`, table), ...)","handlingStrategy":"try-catch","validationCode":"if err := ctx.Err(); err != nil { return err }\ncols, err := tableColumns(tx, table)\nif err != nil { return err }\nfor _, c := range []string{\"id\", \"issue_id\", \"event_type\", \"actor\", \"old_value\", \"new_value\", \"comment\", \"created_at\"} {\n\tif !slices.Contains(cols, c) {\n\t\treturn fmt.Errorf(\"events table %s missing column %s; migrate first\", table, c)\n\t}\n}","typeGuard":"func isQueryErr(err error) bool {\n\tvar me *mysql.MySQLError // dolt speaks mysql protocol\n\treturn errors.As(err, &me)\n}","tryCatchPattern":"id, err := InsertDerivedEvent(ctx, tx, e)\nif err != nil {\n\tif strings.Contains(err.Error(), \"Unknown column\") || strings.Contains(err.Error(), \"doesn't exist\") {\n\t\treturn fmt.Errorf(\"schema drift on %s; run migrations: %w\", table, err)\n\t}\n\treturn err\n}","preventionTips":["Apply all migrations before importing or commenting via derived-event APIs.","Only pass the library's documented table routing constants.","Check transaction health after long operations; don't reuse aborted txs.","Log the unwrapped driver error chain for diagnostics."],"tags":["sql","query-error","storage","derived-events"],"backgroundTag":"sql-query-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}