gastownhall/beads · error

keying dependencies.id for migration 0053: %w

Error message

keying dependencies.id for migration 0053: %w

What it means

Wraps failure of `ALTER TABLE dependencies ADD PRIMARY KEY (id)` — the last step of migration 0053, restoring id as the table's primary key after the drifted key was dropped and all ids were backfilled.

Source

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

		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)
	}
	if hasAnyPrimaryKey {
		// The #4690 drifted shape has dependencies keyed some other way (or
		// keyless): a table can carry only one PRIMARY KEY, so whatever is
		// there must go before id can become it. The uk_dep_* natural-identity
		// unique keys (0043) enforce the real uniqueness independently of
		// whatever this was, so dropping it is safe.
		if _, err := db.ExecContext(ctx, "ALTER TABLE dependencies DROP PRIMARY KEY"); err != nil {
			return fmt.Errorf("dropping dependencies' existing primary key for migration 0053: %w", err)
		}
	}
	if _, err := db.ExecContext(ctx, "ALTER TABLE dependencies ADD PRIMARY KEY (id)"); err != nil {
		return fmt.Errorf("keying dependencies.id for migration 0053: %w", err)
	}
	return nil
}

// firstNonNullString returns the first valid (non-NULL) value among cols, or
// "" if all are NULL.
func firstNonNullString(cols ...sql.NullString) string {
	for _, c := range cols {
		if c.Valid {
			return c.String
		}
	}
	return ""
}

// ensureIssuesRigColumns repairs #4502: the rig/agent columns were only ever
// added to the squashed bootstrap 0001_create_issues, so a database
// bootstrapped before they existed reaches schema v52 without them, and

View on GitHub (pinned to 71377f2769)

Solutions

  1. Rerun `bd` with exclusive access (no other bd processes or SQL sessions)
  2. Check for duplicate natural identities: GROUP BY issue_id, depends_on_issue_id, depends_on_wisp_id, depends_on_external HAVING COUNT(*) > 1 and deduplicate
  3. Verify remainingNull == 0 and no duplicated CHAR(36) ids before retrying
  4. Grant ALTER/INDEX if the wrapped error is a privilege error

Example fix

// before: duplicate natural-identity rows collide on derived id
bd dolt sql -q "SELECT issue_id, depends_on_issue_id, COUNT(*) c FROM dependencies GROUP BY 1,2 HAVING c > 1"
// after: deduplicate, then rerun
bd ready
Defensive patterns

Strategy: validation

Validate before calling

-- must return zero rows before the repair can key dependencies.id
SELECT issue_id, depends_on_issue_id, depends_on_wisp_id, depends_on_external, COUNT(*) c
FROM dependencies
GROUP BY 1,2,3,4
HAVING c > 1;
SELECT COUNT(*) FROM dependencies WHERE id IS NULL;

Try / catch

if err := ensureSchema(ctx, db); err != nil {
    if strings.Contains(err.Error(), "keying dependencies.id for migration 0053") {
        if strings.Contains(err.Error(), "Duplicate") { return fmt.Errorf("dedupe natural-identity rows, then retry") }
        return retryWithBackoff(ensureSchema)
    }
    return err
}

Prevention

When it happens

Trigger: ADD PRIMARY KEY fails: duplicate non-NULL id values exist (backfill produced a collision), a NULL id slipped through, the table is locked by another writer, privileges are missing, or the connection dropped during the ALTER.

Common situations: Concurrent bd sessions inserting dependencies during the repair; a previously interrupted run that keyed rows inconsistently; manual SQL inserted duplicate natural-identity rows so the derived ids collide.

Related errors


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