{"record":{"id":"926f6543fd557b8b","repo":"gastownhall/beads","slug":"reading-pre-migration-schema-version-w","errorCode":null,"errorMessage":"reading pre-migration schema version: %w","messagePattern":"reading pre-migration schema version: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/schema.go","lineNumber":652,"sourceCode":"\t}\n\ttouchedDirtyTables, err := mainSource.pendingMigrationDirtyTables(ctx, db, dirtyBefore)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"checking dirty tables against pending migrations: %w\", err)\n\t}\n\tif len(touchedDirtyTables) > 0 {\n\t\treturn 0, &DirtyTablesError{Tables: touchedDirtyTables}\n\t}\n\tdirtyBeforeSignatures, err := dirtyTableSignatures(ctx, db, dirtyBefore)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"reading pre-migration dirty table diffs: %w\", err)\n\t}\n\t// Captured before the main migrations run: the aux re-key uses it to\n\t// distinguish the lineage's first rekey-aware migration (run the pass)\n\t// from a fresh clone of an already-converged lineage (record the marker\n\t// only, bd-578h9.4).\n\tmainVersionBefore, err := mainSource.currentVersion(ctx, db)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"reading pre-migration schema version: %w\", err)\n\t}\n\n\tapplied, mainColumnAdded, err := mainSource.migrate(ctx, db, 0)\n\tif err != nil {\n\t\treturn applied, err\n\t}\n\n\tbackfilled, err := ensureBackfilledCustomStatusesCustomTypes(ctx, db)\n\tif err != nil {\n\t\treturn applied, fmt.Errorf(\"backfill custom tables: %w\", err)\n\t}\n\n\t// #4259: rewrite any per-clone-random dependency ids (minted by 0043's\n\t// DEFAULT (UUID()) before this fix) to the deterministic key, so independently\n\t// migrated clones converge to byte-identical, merge-safe dependencies. Runs\n\t// here, after the schema migrations (0050 has asserted the canonical schema),\n\t// and only on a pass where migration work was needed.\n\trekeyed, err := rekeyDependencyIDs(ctx, db)","sourceCodeStart":634,"sourceCodeEnd":670,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/schema.go#L634-L670","documentation":"This error wraps a failure reading the current main-source schema version (mainSource.currentVersion) before any migration runs. The captured version lets the aux re-key distinguish a first rekey-aware pass from a fresh clone of an already-converged lineage (bd-578h9.4). Without it, MigrateUp cannot run safely and aborts with nothing applied.","triggerScenarios":"Calling MigrateUp/MigrateUpWithLock when the schema version cursor cannot be read - missing or unreadable schema_migrations table, corrupt version row, or a Dolt query error on the cursor table.","commonSituations":"Databases copied out-of-band without the schema_migrations table; a manual edit/deletion of the migrations cursor; storage-level failures on a read-only or damaged repo.","solutions":["Fix the wrapped cause (query failure on the version cursor table)","Ensure the schema_migrations table exists and contains a valid version row; recreate it from a healthy clone if missing","Never hand-edit migration cursors - let migrations own them","Retry MigrateUp once cursor reads succeed"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Confirm the migration cursor exists before opening\nrow := db.QueryRowContext(ctx,\n    \"SELECT COUNT(*) FROM schema_migrations\")\nvar n int\nif err := row.Scan(&n); err != nil || n == 0 {\n    return errors.New(\"schema_migrations missing or empty; restore from a healthy clone\")\n}","typeGuard":"func isVersionCursorErr(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"reading pre-migration schema version\")\n}","tryCatchPattern":"if _, err := schema.MigrateUp(ctx, db); err != nil {\n    if isVersionCursorErr(err) {\n        return fmt.Errorf(\"migration cursor broken, refusing to guess version: %w\", err)\n    }\n    return err\n}","preventionTips":["Never hand-edit or delete schema_migrations rows","Copy whole databases, not individual tables, so cursors travel with data","Let MigrateUp exclusively manage version state"],"tags":["database","migration","dolt","schema-version"],"backgroundTag":"migration-cursor-unreadable","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}