gastownhall/beads · error
scan schema_migrations conflict: %w
Error message
scan schema_migrations conflict: %w
What it means
Returned by schemaMigrationsConflictsAreVintageOnly when rows.Scan cannot read a dolt_conflicts_schema_migrations row into (NullInt64, NullInt64, NullString, NullString) for our_version, their_version, our_content_hash, their_content_hash. This indicates the conflict row's shape or types don't match the expected four-column vintage-check layout, so the function aborts rather than risk auto-resolving a real content fork (the #4259 schema fork) as if it were a vintage artifact.
Source
Thrown at internal/storage/versioncontrolops/mergesettle.go:732
// whose content hashes are compatible: equal, or NULL/empty on exactly one side
// (a pre-#4270 binary recorded the version without a hash, bd-6dnrw.29). Two
// different non-empty hashes mean the clones applied different content for the
// same version — the #4259 schema fork — and are never auto-resolved. A row
// deleted on one side is not a vintage artifact either.
func schemaMigrationsConflictsAreVintageOnly(ctx context.Context, db DBConn) (bool, error) {
rows, err := db.QueryContext(ctx, `
SELECT our_version, their_version, our_content_hash, their_content_hash
FROM dolt_conflicts_schema_migrations`)
if err != nil {
return false, fmt.Errorf("query schema_migrations conflicts: %w", err)
}
defer rows.Close()
for rows.Next() {
var ourVersion, theirVersion sql.NullInt64
var ourHash, theirHash sql.NullString
if err := rows.Scan(&ourVersion, &theirVersion, &ourHash, &theirHash); err != nil {
return false, fmt.Errorf("scan schema_migrations conflict: %w", err)
}
if !ourVersion.Valid || !theirVersion.Valid || ourVersion.Int64 != theirVersion.Int64 {
return false, nil
}
ours, theirs := ourHash.String, theirHash.String
if ours != "" && theirs != "" && ours != theirs {
return false, nil // real content skew (#4259) — operator decides
}
}
return true, rows.Err()
}
// resolveSchemaMigrationsVintageConflicts resolves vintage-only cursor-row
// conflicts (validated by schemaMigrationsConflictsAreVintageOnly) keeping
// whichever side recorded a content hash: when theirs has the hash and ours is
// NULL, the working-set row is updated to theirs before the table-level
// resolve, so '--ours' never discards recorded provenance.
func resolveSchemaMigrationsVintageConflicts(ctx context.Context, db DBConn) error {View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped driver error: a column-count/type mismatch means the repo's conflict-table layout differs — run the appropriate bd migration/doctor step or align Dolt versions.
- Retry after reconnecting; mid-iteration driver errors poison subsequent Scan calls.
- Resolve schema_migrations conflicts manually with dolt conflicts resolve after manually comparing versions and content hashes.
- If a crashed merge left partial conflict rows, abort/reset the merge (dolt merge --abort) and re-merge.
- For test harnesses, ensure mock rows return exactly four columns matching NullInt64/NullString targets.
Defensive patterns
Strategy: validation
Validate before calling
// Confirm the conflict table exposes the four expected columns before scanning:
rows, err := db.QueryContext(ctx,
"SELECT column_name FROM information_schema.columns WHERE table_name = 'dolt_conflicts_schema_migrations'")
// require our_version, their_version, our_content_hash, their_content_hash;
// if absent, resolve schema_migrations conflicts manually instead of auto-resolving. Try / catch
if err := TryAutoResolveMergeConflicts(ctx, db); err != nil {
if strings.Contains(err.Error(), "scan schema_migrations conflict") {
// row shape mismatch — abort the merge and resolve manually:
// dolt merge --abort; dolt conflicts resolve ...
}
} Prevention
- Keep bd and the Dolt engine on compatible versions to avoid conflict-table layout drift.
- After a crashed merge, abort and re-merge rather than scanning partial conflict rows.
- Always check rows.Err() after iteration loops to catch poisoning early.
- Validate mock drivers in tests return four columns matching NullInt64/NullString targets.
- Manually compare migration content hashes before accepting any auto-resolution.
When it happens
Trigger: TryAutoResolveMergeConflicts iterates dolt_conflicts_schema_migrations and Scan fails — column count or type mismatch from a Dolt version whose conflict table differs (e.g. pre-#4270 repos without content_hash on both sides), a driver that can't convert a version column into sql.NullInt64, or scanning after the rows iterator already errored.
Common situations: Repos created by older bd binaries where schema_migrations lacked content_hash; mixed-version clusters where the merge was written by a different Dolt engine; test doubles returning wrong column counts; corrupt or partially-written conflict rows after a crashed merge.
Related errors
- scan config conflict: %w
- query schema_migrations conflicts: %w
- failed to get conflicts: %w
- failed to scan conflict: %w
- iter dependents: scan: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/41a7d5479bd2d942.
Report an issue: GitHub.