gastownhall/beads · error

cascade-repair %s: %w

Error message

cascade-repair %s: %w

What it means

TryRepairFKCascadeViolations repairs foreign-key constraint violations caused by pull merges by deleting orphaned rows, then clearing the recorded violations. This error wraps a failure of the per-table cascade delete statement (from the fkCascadeRepairDeletes allowlist). The repair aborted for that table and the merge settlement reports repair-failed.

Source

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

	// Validate every violating table before touching any of them.
	for _, t := range tables {
		if _, ok := fkCascadeRepairDeletes[t]; !ok {
			return false, true, nil
		}
		issueFKOnly, err := violationsAreIssueFKOnly(ctx, db, t)
		if err != nil {
			return false, true, err
		}
		if !issueFKOnly {
			return false, true, nil
		}
	}

	for _, t := range tables {
		res, err := db.ExecContext(ctx, fkCascadeRepairDeletes[t])
		if err != nil {
			return false, true, fmt.Errorf("cascade-repair %s: %w", t, err)
		}
		n, _ := res.RowsAffected()
		// t is from the fixed fkCascadeRepairDeletes allowlist, never user input.
		//nolint:gosec // G201/G202: hardcoded table name.
		if _, err := db.ExecContext(ctx, "DELETE FROM dolt_constraint_violations_"+t); err != nil {
			return false, true, fmt.Errorf("clear %s constraint violations: %w", t, err)
		}
		//nolint:gosec // G202: hardcoded table name.
		if _, err := db.ExecContext(ctx, "CALL DOLT_ADD('"+t+"')"); err != nil {
			return false, true, fmt.Errorf("stage repaired %s: %w", t, err)
		}
		fmt.Fprintf(os.Stderr,
			"Notice: pull merged %s row(s) referencing issue(s) deleted on another clone; applied the foreign key's cascade delete (%d row(s) removed)\n",
			t, n)
	}

	// The repair must leave nothing behind: a residual violation here means the
	// deletes above did not cover the constraint that fired, and committing

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped %w driver error for the actual SQL failure reason.
  2. Run the DELETE manually inside a transaction to see which rows/constraints block it.
  3. Check dolt_constraint_violations_<table> contents to confirm the violations are FK-only before repair.
  4. Retry the merge/settle after the blocking transaction ends.

Example fix

// before: single-shot delete
res, err := db.ExecContext(ctx, fkCascadeRepairDeletes[t])
// after: verify violation type first and surface engine detail
ok, err := violationsAreIssueFKOnly(ctx, db, t)
if err != nil || !ok {
    return false, false, fmt.Errorf("cascade-repair %s: violations not FK-only, refusing auto-delete", t)
}
Defensive patterns

Strategy: try-catch

Validate before calling

var n int
_ = db.QueryRow("SELECT COUNT(*) FROM dolt_constraint_violations WHERE `table` = ? AND num_violations > 0", "issues").Scan(&n)
// if n > 0, inspect violations manually before enabling auto-repair

Try / catch

repaired, hasConflicts, err := TryRepairFKCascadeViolations(ctx, db)
if err != nil && strings.Contains(err.Error(), "cascade-repair") {
    // inspect dolt_constraint_violations_<table> by hand; do not force-settle
    return manualViolationReview(err)
}

Prevention

When it happens

Trigger: The hardcoded DELETE for table t fails — e.g. additional constraint violations on the row, engine-level FK enforcement rejecting the delete, table doesn't exist after a schema merge, or lock/transaction errors.

Common situations: Pull merged issues referencing other issues deleted on another clone; concurrent writer holds locks; Dolt engine refuses deletes while constraint violations are recorded for that table.

Related errors


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