gastownhall/beads · error

wisp_dependencies: %w

Error message

wisp_dependencies: %w

What it means

Same family as the dependencies error but for wisp_dependencies: rekeyDependencyIDs wraps failures from re-keying the wisp_dependencies edge table. Note wisp_dependencies is dolt-ignored, so this re-key stays clone-local and does not participate in pull-based convergence — a failure here affects only the local clone.

Source

Thrown at internal/storage/schema/dep_id_backfill.go:36

// id. Leaving them would keep two independently-migrated clones divergent (same
// edge, different primary key) and break `bd dolt pull`. This rewrites them to
// the deterministic value so two clones converge to byte-identical dependencies.
//
// It runs from MigrateUp right after the schema migrations (so 0050 has already
// asserted the canonical schema), and only on a pass where migration work was
// needed — it is not part of the steady-state open path. It is idempotent: a row
// already keyed deterministically is skipped, so re-running on a later migration
// pass is a cheap no-op. dependencies changes are staged and committed by
// MigrateUp; wisp_dependencies is dolt-ignored, so its re-key stays clone-local
// (it only escapes on promotion, which copies the id).
func rekeyDependencyIDs(ctx context.Context, db DBConn) (bool, error) {
	wroteDeps, err := rekeyDependencyTable(ctx, db, "dependencies")
	if err != nil {
		return wroteDeps, fmt.Errorf("dependencies: %w", err)
	}
	wroteWisp, err := rekeyDependencyTable(ctx, db, "wisp_dependencies")
	if err != nil {
		return wroteDeps || wroteWisp, fmt.Errorf("wisp_dependencies: %w", err)
	}
	return wroteDeps || wroteWisp, nil
}

// rekeyDependencyTable re-derives ids for one edge table. table must be a
// hardcoded constant ("dependencies" or "wisp_dependencies").
func rekeyDependencyTable(ctx context.Context, db DBConn, table string) (bool, error) {
	// Skip cleanly if the table or its id column isn't present (e.g. an older or
	// partial schema where the surrogate key was never added): nothing to re-key.
	hasID, err := columnExists(ctx, db, table, "id")
	if err != nil {
		return false, err
	}
	if !hasID {
		return false, nil
	}

	// The natural target is the single non-null of the three typed columns —

View on GitHub (pinned to 71377f2769)

Solutions

  1. Inspect the wrapped inner error for the specific failing statement
  2. Check for duplicate wisp edges that would collide on the deterministic id and deduplicate them
  3. Re-run the migration pass — the re-key is idempotent
  4. If wisp_dependencies lacks the id column (older schema), the pass skips cleanly; verify with the INFORMATION_SCHEMA.COLUMNS query
Defensive patterns

Strategy: try-catch

Validate before calling

-- same preflight for wisp_dependencies (clone-local)
SELECT issue_id, COALESCE(depends_on_issue_id, depends_on_wisp_id, depends_on_external) t, COUNT(*) c
FROM wisp_dependencies GROUP BY issue_id, t HAVING c > 1;

Try / catch

if _, err := rekeyDependencyIDs(ctx, db); err != nil {
    if dberrors.IsUniqueViolation(err) {
        return fmt.Errorf("dedupe wisp edges: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: rekeyDependencyTable(ctx, db, "wisp_dependencies") fails — the same failure modes as the dependencies pass: schema probe error, SELECT failure, scan error, or an UPDATE re-key failing (e.g. unique-key collision on depid-derived ids).

Common situations: Old clones carrying UUID-keyed wisp rows from before migration 0050; connection drops mid-migration; duplicate wisp edges resolving to the same deterministic id; partial/older schema lacking the typed target columns.

Related errors


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