{"record":{"id":"848e555f6276221e","repo":"gastownhall/beads","slug":"scan-conflict-row-for-table-s-w","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/automerge.go","lineNumber":116,"sourceCode":"\trows, err := db.QueryContext(ctx, \"SELECT * FROM `dolt_conflicts_\"+table+\"`\") //nolint:gosec // table validated as an identifier above\n\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\tvar out []rawConflictRow\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, rawConflictRow{cols: cols, vals: 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// duplicateConflictKey reports the first our-side key held by more than one\n// live conflict row. Both resolvers settle a row by deleting its conflict BY\n// KEY, so two rows sharing one key would both be cleared by the first delete\n// and make the second iteration abort on \"no conflict row deleted\" — a message\n// about the wrong thing entirely, after a row was resolved without ever being\n// merged. loadConflictRow refuses the same shape on the operator's single-row\n// path (conflicts.go); the auto-merge pre-screens instead DECLINE on it, which\n// is this file's idiom and what lets the caller still build the\n// MergeConflictsError that tells an operator which tables need them.","sourceCodeStart":98,"sourceCodeEnd":134,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/versioncontrolops/automerge.go#L98-L134","documentation":"loadConflictRows scans each conflict row into generic []any pointers; this error wraps a rows.Scan failure for a specific table. Scanning fails when a column's value cannot be converted into a generic any slot — typically NULL-shape or type issues in the conflict row produced by dolt, or column-count mismatch when a peer's schema merge changed the conflicts table shape.","triggerScenarios":"Auto-resolve reads dolt_conflicts_issues (or labels/comments/events) and a row cannot be scanned — a peer's schema merge extended the conflict table's columns so the scan buffer mismatches, or the conflict row contains a value type the driver will not scan into any.","commonSituations":"Merging branches where the schema itself changed (new columns) so dolt_conflicts tables carry extra base/our/their metadata; corrupted conflict rows after a crash.","solutions":["Inspect the wrapped cause to see which column/type failed to scan","Abort and resolve the merge manually with dolt's table-level resolution (DOLT_CONFLICTS_RESOLVE --ours/--theirs), then retry","Align both branches' schemas before merging so conflict rows keep the expected shape","Upgrade dolt/beads if the conflict-row format changed between versions"],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"// Check the conflict table's column count matches the base table's\n// expected shape before auto-resolve\nrows, _ := db.QueryContext(ctx, \"SELECT * FROM dolt_conflicts_issues LIMIT 1\")\ncols, _ := rows.Columns()\nrows.Close()\nif len(cols) < expectedMinConflictColumns { /* shape drifted; use manual resolution */ }","typeGuard":null,"tryCatchPattern":"err := versioncontrolops.TryAutoResolveMergeConflicts(ctx, db)\nif err != nil && strings.Contains(err.Error(), \"scan conflict row for table\") {\n    // conflict-row shape unexpected (schema merge): fall back to\n    // manual table-level resolution\n    return manualResolve(ctx, db)\n}","preventionTips":["Keep both branches' schemas identical before merging","Apply schema changes on one side at a time and merge promptly","Avoid third-party tools writing arbitrary columns into the issues table","Pin compatible dolt versions across collaborators"],"tags":["dolt","merge-conflicts","schema"],"backgroundTag":"conflict-row-scan-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}