gastownhall/beads · error

scan dependency conflict: %w

Error message

scan dependency conflict: %w

What it means

While iterating rows of dolt_conflicts_dependencies, rows.Scan into 12 sql.NullString columns (both sides of issue/wisp/external dependency fields plus type) failed. Scan errors mean the result set shape or types do not match the scan targets — typically a column-count mismatch or an unconvertible type. The routine returns false so conflicting rows are left for operator review.

Source

Thrown at internal/storage/versioncontrolops/mergesettle.go:625

		FROM dolt_conflicts_dependencies`)
	if err != nil {
		return false, fmt.Errorf("query dependency conflicts: %w", err)
	}
	defer rows.Close()

	for rows.Next() {
		var (
			ourID, theirID             sql.NullString
			ourIssue, theirIssue       sql.NullString
			ourDepIssue, theirDepIssue sql.NullString
			ourDepWisp, theirDepWisp   sql.NullString
			ourDepExt, theirDepExt     sql.NullString
			ourType, theirType         sql.NullString
		)
		if err := rows.Scan(&ourID, &theirID, &ourIssue, &theirIssue,
			&ourDepIssue, &theirDepIssue, &ourDepWisp, &theirDepWisp,
			&ourDepExt, &theirDepExt, &ourType, &theirType); err != nil {
			return false, fmt.Errorf("scan dependency conflict: %w", err)
		}
		// One side deleted the edge (add/delete conflict): leave for the operator.
		if !ourID.Valid || !theirID.Valid {
			return false, nil
		}
		// Same edge requires the same source issue. A differing issue_id means the
		// shared id is stale on one side (e.g. a rename), not a shared edge.
		if ourIssue.Valid != theirIssue.Valid || ourIssue.String != theirIssue.String {
			return false, nil
		}
		// ...and the same resolved target.
		ourTarget, ourOK := resolveConflictDepTarget(ourDepIssue, ourDepWisp, ourDepExt)
		theirTarget, theirOK := resolveConflictDepTarget(theirDepIssue, theirDepWisp, theirDepExt)
		if ourOK != theirOK || ourTarget != theirTarget {
			return false, nil
		}
		// A differing type is the only remaining way this is a real semantic conflict.
		if ourType.Valid != theirType.Valid || ourType.String != theirType.String {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Run DESCRIBE dolt_conflicts_dependencies and match the SELECT column list and order exactly to the 12 Scan targets.
  2. Upgrade or align the Dolt/driver version so the conflict-table schema matches what the query expects.
  3. Scan into sql.RawBytes or interface{} where types are uncertain, then convert explicitly instead of relying on NullString conversion.
  4. Check the wrapped inner error: 'sql: expected N destination arguments' means a count mismatch; 'converting driver.Value type' means a type mismatch.

Example fix

// before
if err := rows.Scan(&ourID, &theirID, &ourIssue, &theirIssue,
	&ourDepIssue, &theirDepIssue, &ourDepWisp, &theirDepWisp,
	&ourDepExt, &theirDepExt, &ourType, &theirType); err != nil {
	return false, fmt.Errorf("scan dependency conflict: %w", err)
}
// after
cells := make([]any, 12)
ptrs := make([]any, 12)
for i := range cells {
	ptrs[i] = &cells[i]
}
if err := rows.Scan(ptrs...); err != nil {
	return false, fmt.Errorf("scan dependency conflict: %w", err)
}
toNull := func(v any) sql.NullString {
	s, _ := v.(string)
	if v == nil { return sql.NullString{} }
	return sql.NullString{String: fmt.Sprintf("%v", v), Valid: true}
}
ourID, theirID := toNull(cells[0]), toNull(cells[1])
Defensive patterns

Strategy: type-guard

Validate before calling

cols, err := rows.Columns()
if err != nil || len(cols) != 12 {
	// schema drift: do not scan; fall back to operator review
}

Type guard

func scanDependencyConflictRow(rows *sql.Rows) (ok bool) {
	cols, err := rows.Columns()
	if err != nil || len(cols) != 12 {
		return false
	}
	cells := make([]any, len(cols))
	ptrs := make([]any, len(cols))
	for i := range cells {
		ptrs[i] = &cells[i]
	}
	return rows.Scan(ptrs...) == nil
}

Try / catch

auditOnly, err := dependencyConflictsAreAuditOnly(ctx, db)
if err != nil && strings.Contains(err.Error(), "scan dependency conflict") {
	// schema mismatch: log and treat conflicts as operator-resolvable
	auditOnly = false
}

Prevention

When it happens

Trigger: dependencyConflictsAreAuditOnly reads a row whose column count differs from the 12 expected values (Dolt schema drift), or a column type (e.g. integer/boolean instead of string) cannot scan into sql.NullString.

Common situations: Dolt version upgrade changed dolt_conflicts_dependencies columns (added/removed/reordered); a column became non-nullable-typed (e.g. BIGINT) and the driver refuses to convert into NullString; a driver that returns different types for conflict tables than for normal tables.

Related errors


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