gastownhall/beads · error

stage resolved %s: %w

Error message

stage resolved %s: %w

What it means

After a strategy resolution succeeds, SettleMerge stages the table with `CALL DOLT_ADD(?)`. "stage resolved %s: %w" wraps DOLT_ADD's failure. The resolution itself worked but the resolved file could not be staged, so the merge is aborted and the working set restored rather than committing a half-staged state.

Source

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

				return &MergeConflictsError{Conflicts: conflicts, MergeErr: mergeErr}
			}
			// #4992 part 2: the operator asked for an escape hatch. Unlike
			// TryAutoResolveMergeConflicts, no allowlist applies — every
			// conflicted table (the resolver pre-screens ALL of them before
			// resolving any, so `resolved == false` means none were touched)
			// is resolved with the named strategy.
			for _, c := range conflicts {
				table := c.Field
				if table == "" {
					table = "issues"
				}
				if err := ResolveConflicts(ctx, db, table, strategy); err != nil {
					abortMerge(ctx, db, preMergeClean)
					return fmt.Errorf("resolve %s conflicts with '%s' strategy: %w", table, strategy, err)
				}
				if _, err := db.ExecContext(ctx, "CALL DOLT_ADD(?)", table); err != nil {
					abortMerge(ctx, db, preMergeClean)
					return fmt.Errorf("stage resolved %s: %w", table, err)
				}
			}
			strategyResolved = true
		}
	}

	// bd-6dnrw.4: repair FK cascade violations the merge produced (child rows
	// whose parent issue was deleted on the other clone). Unrepaired
	// violations MUST NOT survive: with the force flag on, every statement
	// autocommits, so the abort below is what keeps them out of the database.
	// This also covers violations a strategy resolution left behind (e.g.
	// --ours keeps a child row whose parent was deleted on the other side).
	repairedViol, hadViol, violErr := TryRepairFKCascadeViolations(ctx, db)
	if violErr != nil {
		abortMerge(ctx, db, preMergeClean)
		if mergeErr != nil {
			return mergeErr
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry the merge — the abort restored the working set, so a clean retry usually succeeds
  2. Ensure no other bd process operates on the database concurrently during pull/merge
  3. Check the wrapped error for the DOLT_ADD root cause (unknown table, storage error)
  4. Upgrade Dolt if the parameterized CALL DOLT_ADD(?) form is rejected by your version
Defensive patterns

Strategy: retry

Validate before calling

// ensure exclusive access before merging
var busy int
_ = db.QueryRowContext(ctx, "SELECT COUNT(*) FROM information_schema.processlist WHERE db = DATABASE() AND id != CONNECTION_ID()").Scan(&busy)
if busy > 0 {
	return errors.New("other sessions are using this database; retry when idle")
}

Type guard

func isStagingFailure(err error) bool { return strings.Contains(err.Error(), "stage resolved ") }

Try / catch

err := versioncontrolops.MergeAndSettleWithStrategy(ctx, db, ref, strategy)
if err != nil && isStagingFailure(err) {
	// merge was aborted; working set restored — safe to retry
	err = versioncontrolops.MergeAndSettleWithStrategy(ctx, db, ref, strategy)
}

Prevention

When it happens

Trigger: DOLT_ADD failing on a table name (empty Field defaulting to 'issues' not matching the actual conflict table), a Dolt server error, or the merge state having been closed by another session between resolve and add.

Common situations: Concurrent bd sessions mutating the same repo mid-merge; Dolt server-side storage errors; conflicts reported on a table whose name DOLT_ADD rejects (quoting/parameter issues in odd Dolt versions).

Related errors


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