{"record":{"id":"c559d8deff6211a2","repo":"gastownhall/beads","slug":"finalizing-dependencies-id-for-migration-0053-w","errorCode":null,"errorMessage":"finalizing dependencies.id for migration 0053: %w","messagePattern":"finalizing dependencies\\.id for migration 0053: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/migration_repairs.go","lineNumber":535,"sourceCode":"\t}\n\tif remainingNull > 0 {\n\t\t// Fail with an actionable count now rather than let a subsequent\n\t\t// MODIFY COLUMN ... NOT NULL below abort with a generic \"column\n\t\t// cannot be null\" error, or silently key the table while leaving\n\t\t// NULL-id rows behind it.\n\t\treturn 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)\n\t}\n\n\tidIsPrimaryKey, err := schemaColumnInPrimaryKey(ctx, db, \"dependencies\", \"id\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"checking dependencies.id primary key: %w\", err)\n\t}\n\tif idIsPrimaryKey {\n\t\treturn nil\n\t}\n\n\tif _, err := db.ExecContext(ctx, \"ALTER TABLE dependencies MODIFY COLUMN id CHAR(36) NOT NULL\"); err != nil {\n\t\treturn fmt.Errorf(\"finalizing dependencies.id for migration 0053: %w\", err)\n\t}\n\n\thasAnyPrimaryKey, err := schemaHasPrimaryKey(ctx, db, \"dependencies\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"checking dependencies for an existing primary key: %w\", err)\n\t}\n\tif hasAnyPrimaryKey {\n\t\t// The #4690 drifted shape has dependencies keyed some other way (or\n\t\t// keyless): a table can carry only one PRIMARY KEY, so whatever is\n\t\t// there must go before id can become it. The uk_dep_* natural-identity\n\t\t// unique keys (0043) enforce the real uniqueness independently of\n\t\t// whatever this was, so dropping it is safe.\n\t\tif _, err := db.ExecContext(ctx, \"ALTER TABLE dependencies DROP PRIMARY KEY\"); err != nil {\n\t\t\treturn fmt.Errorf(\"dropping dependencies' existing primary key for migration 0053: %w\", err)\n\t\t}\n\t}\n\tif _, err := db.ExecContext(ctx, \"ALTER TABLE dependencies ADD PRIMARY KEY (id)\"); err != nil {\n\t\treturn fmt.Errorf(\"keying dependencies.id for migration 0053: %w\", err)","sourceCodeStart":517,"sourceCodeEnd":553,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/migration_repairs.go#L517-L553","documentation":"Wraps failure of `ALTER TABLE dependencies MODIFY COLUMN id CHAR(36) NOT NULL`, the step that finalizes the id column type/nullability after backfill completes. The ALTER is what locks the column into its 0043/0053 shape before it becomes the primary key.","triggerScenarios":"Backfill succeeded but the MODIFY COLUMN fails: duplicate NULLs were missed (blocked earlier), table locked by another writer, insufficient disk for the table rebuild, Dolt transaction conflict, or connection dropped during the (potentially long) ALTER on a large dependencies table.","commonSituations":"Large databases where ALTER takes minutes and a health-checker restarts bd mid-ALTER; concurrent bd process holding metadata locks; low-disk environments failing the table rebuild.","solutions":["Ensure no other bd/Dolt process is using the database, then rerun — the repair is idempotent and re-checks state each time","Free disk space; ALTER TABLE rebuilds the table and needs headroom","For very large tables, run the migration in a maintenance window and disable process supervisors that kill long-running bd","Check the wrapped %w error for the precise server-side reason and act on it"],"exampleFix":"// before: ALTER killed by systemd timeout\nsudo systemctl stop beads-timer  # or stop the scheduler restarting bd\nbd ready                        // completes migration\n// after: restart services\nsudo systemctl start beads-timer","handlingStrategy":"retry","validationCode":"df -h /path/to/db   # ensure headroom for table rebuild\nbd dolt sql -q 'SELECT COUNT(*) FROM dependencies' || echo 'db busy/unreachable'","typeGuard":null,"tryCatchPattern":"if err := ensureSchema(ctx, db); err != nil {\n    if strings.Contains(err.Error(), \"finalizing dependencies.id for migration 0053\") {\n        // verify state, then resume — repair is idempotent\n        return retryWithBackoff(ensureSchema)\n    }\n    return err\n}","preventionTips":["Disable aggressive supervisors/timers during migration windows","Keep disk headroom > size of largest table","Serialize upgrades: one writer, one migration run","Increase lock wait timeout if dependencies is heavily written"],"tags":["database","migration","alter-table","sql"],"backgroundTag":"migration-backfill-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}