{"record":{"id":"656e529f5337645c","repo":"gastownhall/beads","slug":"failed-to-scan-conflict-w-656e52","errorCode":null,"errorMessage":"failed to scan conflict: %w","messagePattern":"failed to scan conflict: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/versioncontrolops/mergesettle.go","lineNumber":444,"sourceCode":"// The resolved tables are staged but NOT committed: the caller must run\n// CommitResolvedConflicts after the FK cascade repair, because DOLT_COMMIT\n// refuses a working set with outstanding constraint violations (bd-578h9.14).\nfunc TryAutoResolveMergeConflicts(ctx context.Context, db DBConn) (bool, error) {\n\trows, err := db.QueryContext(ctx, \"SELECT `table`, num_conflicts FROM dolt_conflicts\")\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to query conflicts: %w\", err)\n\t}\n\n\ttype conflict struct {\n\t\ttable string\n\t\tcount int\n\t}\n\tvar conflicts []conflict\n\tfor rows.Next() {\n\t\tvar c conflict\n\t\tif err := rows.Scan(&c.table, &c.count); err != nil {\n\t\t\t_ = rows.Close()\n\t\t\treturn false, fmt.Errorf(\"failed to scan conflict: %w\", err)\n\t\t}\n\t\tconflicts = append(conflicts, c)\n\t}\n\t_ = rows.Close()\n\tif err := rows.Err(); err != nil {\n\t\treturn false, err\n\t}\n\n\tif len(conflicts) == 0 {\n\t\treturn false, nil // No conflicts to resolve — error was something else\n\t}\n\n\t// Decide which conflicted tables are safe to auto-resolve. If any conflict is\n\t// not safely resolvable, resolve nothing and let the pull fail.\n\tvar resolvable []string\n\tvar issuesPlan []issuesRowMerge\n\tvar unionPlans map[string][]unionRowKey\n\tfor _, c := range conflicts {","sourceCodeStart":426,"sourceCodeEnd":462,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/versioncontrolops/mergesettle.go#L426-L462","documentation":"While iterating rows from dolt_conflicts, rows.Scan failed to decode a (table, num_conflicts) pair into the local struct — e.g. unexpected column types or NULL values. The function closes the rows and returns this wrapped error instead of continuing with partial data.","triggerScenarios":"A row in dolt_conflicts has NULL `table` or a num_conflicts value that cannot scan into int (type mismatch across Dolt versions).","commonSituations":"Dolt server version where dolt_conflicts columns differ in type/name; corrupted conflict metadata after an interrupted merge.","solutions":["Inspect raw rows: `SELECT \\`table\\`, num_conflicts FROM dolt_conflicts` and look for NULLs/odd types","Upgrade or align the Dolt server version with what bd expects","Clean the interrupted merge state (DOLT_MERGE_ABORT) so conflict metadata is rebuilt on next merge","Scan into sql.NullString/sql.NullInt64 to tolerate NULLs in custom forks"],"exampleFix":"// before\nrows.Scan(&c.table, &c.count) // fails on NULL\n// after\nvar t sql.NullString; var n sql.NullInt64\nrows.Scan(&t, &n)\nc.table, c.count = t.String, int(n.Int64)","handlingStrategy":"type-guard","validationCode":"rows, _ := db.QueryContext(ctx, \"SELECT \\\"table\\\", num_conflicts FROM dolt_conflicts\")\n// verify all rows non-NULL before relying on auto-resolve\n","typeGuard":"func validConflictRow(table string, count int) bool {\n    return table != \"\" && count >= 0\n}","tryCatchPattern":"ok, err := TryAutoResolveMergeConflicts(ctx, db)\nif err != nil && strings.Contains(err.Error(), \"scan conflict\") {\n    // Dolt version mismatch on dolt_conflicts schema — abort merge and upgrade\n    _ = db.ExecContext(ctx, \"CALL DOLT_MERGE_ABORT()\")\n}","preventionTips":["Run a supported Dolt version with the expected dolt_conflicts schema","DOLT_MERGE_ABORT after corrupted/interrupted merges to reset metadata","Wrap Scan with nullable types in custom forks"],"tags":["dolt","conflicts","scan"],"backgroundTag":"sql-row-scan-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}