{"record":{"id":"969791f3682f76d3","repo":"gastownhall/beads","slug":"scan-conflict-row-for-table-s-w-969791","errorCode":null,"errorMessage":"scan conflict row for table %s: %w","messagePattern":"scan conflict row for table (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/versioncontrolops/conflicts.go","lineNumber":139,"sourceCode":"\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"query conflicts for table %s: %w\", table, err)\n\t}\n\tdefer func() { _ = rows.Close() }()\n\n\tcols, err := rows.Columns()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"conflict columns for table %s: %w\", table, err)\n\t}\n\n\tvar out []storage.ConflictRow\n\tfor rows.Next() {\n\t\tvals := make([]any, len(cols))\n\t\tptrs := make([]any, len(cols))\n\t\tfor i := range vals {\n\t\t\tptrs[i] = &vals[i]\n\t\t}\n\t\tif err := rows.Scan(ptrs...); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"scan conflict row for table %s: %w\", table, err)\n\t\t}\n\t\tout = append(out, buildConflictRow(table, cols, vals))\n\t}\n\tif err := rows.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"iterate conflicts for table %s: %w\", table, err)\n\t}\n\treturn out, nil\n}\n\n// buildConflictRow re-presents one raw conflict row as fields. It is pure, so\n// the column-splitting rules are unit-testable without a database.\nfunc buildConflictRow(table string, cols []string, vals []any) storage.ConflictRow {\n\trow := storage.ConflictRow{Table: table}\n\tindex := make(map[string]int, len(cols))\n\tfor i, col := range cols {\n\t\tside, field, ok := splitConflictColumn(col)\n\t\tif !ok {\n\t\t\tcontinue","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/versioncontrolops/conflicts.go#L121-L157","documentation":"GetConflictRows read column metadata fine but failed while scanning one conflict row into the value buffers. rows.Scan returns an error when the number of destination pointers does not match the column count or when a value's type cannot be converted into the destination. The library allocates one generic any pointer per column, so this usually signals a driver scan/convert failure.","triggerScenarios":"A conflict row contains a value the driver cannot scan into *any (rare) or the number of columns changed between Columns() and Scan (schema drift, concurrent DDL); nil handling or driver-level conversion errors on unusual column types.","commonSituations":"Schema of the user table changed while conflicts were being read; dolt version mismatch producing unexpected system-column types in dolt_conflicts_<table>; corrupted or partially-written conflict rows after a crash.","solutions":["Re-run the query after ensuring no concurrent schema changes are happening","Check the wrapped error for which column failed to convert","Verify the dolt server and driver versions match","Re-trigger the merge to regenerate clean conflict rows if they appear corrupted"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"rows, err := versioncontrolops.GetConflictRows(ctx, db, table)\nif err != nil {\n    if strings.Contains(err.Error(), \"scan conflict row\") {\n        return fmt.Errorf(\"conflict schema changed; re-run merge: %w\", err) // schema drift: don't blind-retry\n    }\n    return err\n}","preventionTips":["Avoid concurrent DDL on conflicted tables","Keep dolt server and driver versions aligned","Alert on scan errors — they indicate schema/compat drift, not transient faults"],"tags":["database","dolt","sql","scan"],"backgroundTag":"sql-scan-error","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}