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

  1. 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.
  2. Retry after reconnecting; mid-iteration driver errors poison subsequent Scan calls.
  3. Resolve schema_migrations conflicts manually with dolt conflicts resolve after manually comparing versions and content hashes.
  4. If a crashed merge left partial conflict rows, abort/reset the merge (dolt merge --abort) and re-merge.
  5. 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

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


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/41a7d5479bd2d942. Report an issue: GitHub.