gastownhall/beads · critical

migration 0053: %d dependencies row(s) have no depends_on_is

Error message

migration 0053: %d dependencies row(s) have no depends_on_issue_id/depends_on_wisp_id/depends_on_external target and cannot be assigned an id (ck_dep_one_target should prevent this); repair manually before retrying

What it means

After backfill, some `dependencies` rows still have NULL id because all three target columns (depends_on_issue_id, depends_on_wisp_id, depends_on_external) are NULL — there is nothing to derive an id from. The `ck_dep_one_target` check constraint should make this impossible, so these rows are drifted data. The repair aborts with the row count instead of keying the table with NULL ids.

Source

Thrown at internal/storage/schema/migration_repairs.go:523

// ensureDependenciesIDPrimaryKey finishes restoring dependencies.id to 0043's
// canonical shape: NOT NULL and the table's PRIMARY KEY. It re-verifies both
// independently of whether this pass just backfilled anything, so a re-entry
// after a crash between the backfill and the key (or between MODIFY NOT NULL
// and ADD PRIMARY KEY) finishes the remaining step(s) instead of re-running
// ones already done -- MODIFY COLUMN restating an identical definition and
// re-adding an already-present PRIMARY KEY are otherwise either redundant or
// outright rejected as a duplicate key.
func ensureDependenciesIDPrimaryKey(ctx context.Context, db DBConn) error {
	var remainingNull int
	if err := db.QueryRowContext(ctx, "SELECT COUNT(*) FROM dependencies WHERE id IS NULL").Scan(&remainingNull); err != nil {
		return fmt.Errorf("counting unbackfilled dependencies.id rows for migration 0053: %w", err)
	}
	if remainingNull > 0 {
		// Fail with an actionable count now rather than let a subsequent
		// MODIFY COLUMN ... NOT NULL below abort with a generic "column
		// cannot be null" error, or silently key the table while leaving
		// NULL-id rows behind it.
		return fmt.Errorf("migration 0053: %d dependencies row(s) have no depends_on_issue_id/depends_on_wisp_id/depends_on_external target and cannot be assigned an id (ck_dep_one_target should prevent this); repair manually before retrying", remainingNull)
	}

	idIsPrimaryKey, err := schemaColumnInPrimaryKey(ctx, db, "dependencies", "id")
	if err != nil {
		return fmt.Errorf("checking dependencies.id primary key: %w", err)
	}
	if idIsPrimaryKey {
		return nil
	}

	if _, err := db.ExecContext(ctx, "ALTER TABLE dependencies MODIFY COLUMN id CHAR(36) NOT NULL"); err != nil {
		return fmt.Errorf("finalizing dependencies.id for migration 0053: %w", err)
	}

	hasAnyPrimaryKey, err := schemaHasPrimaryKey(ctx, db, "dependencies")
	if err != nil {
		return fmt.Errorf("checking dependencies for an existing primary key: %w", err)
	}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Find the offending rows: SELECT * FROM dependencies WHERE id IS NULL
  2. Delete genuinely orphaned rows or set one of depends_on_issue_id / depends_on_wisp_id / depends_on_external so exactly one target is present
  3. Verify ck_dep_one_target exists on the table and re-add it if missing
  4. Rerun `bd` — the repair recounts and proceeds when remainingNull == 0

Example fix

// before: drifted rows block migration
// after: repair or remove rows, then retry
bd dolt sql -q "DELETE FROM dependencies WHERE id IS NULL AND depends_on_issue_id IS NULL AND depends_on_wisp_id IS NULL AND depends_on_external IS NULL" ; bd ready
Defensive patterns

Strategy: validation

Validate before calling

-- run before upgrading; must return zero rows
SELECT issue_id, depends_on_issue_id, depends_on_wisp_id, depends_on_external
FROM dependencies
WHERE depends_on_issue_id IS NULL
  AND depends_on_wisp_id IS NULL
  AND depends_on_external IS NULL;

Try / catch

if err := ensureSchema(ctx, db); err != nil {
    var n int
    if _, scanErr := fmt.Sscanf(err.Error(), "migration 0053: %d dependencies row(s)", &n); scanErr == nil {
        return fmt.Errorf("run repair SQL for %d orphan dependency rows, then retry", n)
    }
    return err
}

Prevention

When it happens

Trigger: ensureDependenciesIDPrimaryKey finds remainingNull > 0 during the 0053 repair — typically a pre-constraint database (written before ck_dep_one_target was added) or rows inserted by a buggy/old version or manual SQL bypassing constraints.

Common situations: Databases migrated across several beads versions where ck_dep_one_target never ran; rows hand-inserted with `bd dolt sql` omitting target columns; a failed partial import/merge leaving orphan dependency rows.

Related errors


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