{"record":{"id":"b48d35c7e2152017","repo":"gastownhall/beads","slug":"migration-s-w","errorCode":null,"errorMessage":"migration %s: %w","messagePattern":"migration (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/schema/schema.go","lineNumber":1672,"sourceCode":"\t\t// Snapshotting first makes repair-hook mutations count as this step's\n\t\t// own newly-dirtied work, so they land in the same atomic commit as\n\t\t// the migration and its cursor row.\n\t\tvar dirtyBeforeStep map[string]dirtyTableState\n\t\tif commitEachStep {\n\t\t\tdirtyBeforeStep, err = dirtyTables(ctx, db, true)\n\t\t\tif err != nil {\n\t\t\t\treturn count, fmt.Errorf(\"snapshotting dirty tables before %s: %w\", mf.name, err)\n\t\t\t}\n\t\t}\n\n\t\tif err := src.preMigrationRepair(ctx, db, mf.version); err != nil {\n\t\t\treturn count, fmt.Errorf(\"pre-repair for migration %s: %w\", mf.name, err)\n\t\t}\n\n\t\tfmt.Fprintf(stderr, \"Applying migration %04d: %s…\\n\", mf.version, humanMigrationName(mf.name))\n\t\tstart := time.Now()\n\t\tif err := execMigrationBody(ctx, db, string(data)); err != nil {\n\t\t\treturn count, fmt.Errorf(\"migration %s: %w\", mf.name, err)\n\t\t}\n\t\tsum := sha256.Sum256(data)\n\t\tcontentHash := hex.EncodeToString(sum[:])\n\t\tif _, err := db.ExecContext(ctx, \"INSERT IGNORE INTO \"+src.cursorTable+\" (version, content_hash) VALUES (?, ?)\", mf.version, contentHash); err != nil {\n\t\t\treturn count, fmt.Errorf(\"recording %s in %s: %w\", mf.name, src.cursorTable, err)\n\t\t}\n\t\tcount++\n\n\t\t// commitEachStep's DOLT_ADD/DOLT_COMMIT is the expensive, fallible\n\t\t// part of this step on the production embedded path. The \"done\" line\n\t\t// (and its timing) must land after that commit succeeds, not before\n\t\t// it: printing \"done\" and then hitting a commit error would show an\n\t\t// operator a false completion, and timing that stopped before the\n\t\t// commit would understate the step's real cost. A failed commit\n\t\t// returns before either print statement below runs.\n\t\tif commitEachStep {\n\t\t\tif err := commitMigrationStep(ctx, db, src.cursorTable, mf.name, dirtyBeforeStep); err != nil {\n\t\t\t\treturn count, fmt.Errorf(\"committing migration %s: %w\", mf.name, err)","sourceCodeStart":1654,"sourceCodeEnd":1690,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/schema/schema.go#L1654-L1690","documentation":"This error wraps a failure that occurred while executing a schema migration's SQL body during runMigrations (internal/storage/schema/schema.go:1672). The library applies each pending numbered migration via execMigrationBody and aborts the whole pass when a migration's SQL fails, returning the count of migrations applied so far. The wrapped inner error carries the underlying database failure (syntax, constraint, lock, timeout, etc.); the outer text identifies which migration file failed.","triggerScenarios":"A migration file's SQL fails when run by execMigrationBody during `bd` startup/upgrade: invalid or incompatible DDL for the current Dolt version, a statement violating constraints, a conflicting schema state (e.g. column/table already exists), or the DB connection/context erroring mid-statement.","commonSituations":"Upgrading beads after skipping intermediate migrations; a partially applied migration from a previous crashed run leaving objects behind; custom or hand-edited migration files; a stale or corrupted embedded Dolt database; running an old `bd` binary against a database already migrated by a newer version.","solutions":["Read the wrapped inner error to identify the failing SQL statement; fix the root database error it names.","Inspect the database state for debris from the failed migration (partially created tables/columns) and reconcile so the migration is idempotent or can re-run.","Verify the `bd` binary and the database's schema_cursor versions are consistent; upgrade `bd` rather than downgrading past applied migrations.","Restore the database from backup and retry the migration pass if the schema is in an unrecoverable dirty state.","If a specific migration file is corrupt or wrong, fix or replace that migration file in the embedded migration source (report upstream if it shipped with the binary)."],"exampleFix":"// before: unclear which migration/statement failed, retries blindly\nbd migrate up  # fails: migration 0104_add_index: syntax error at ...\n// after: inspect and reconcile the failing statement's schema state first\nmysql -d .beads/beads.db -e \"SHOW TABLES; SHOW CREATE TABLE issues;\"\n# drop the partial object, fix the DB state, then re-run the migration pass\nbd migrate up","handlingStrategy":"try-catch","validationCode":"// Before running migrations, verify DB reachability and version alignment\nif err := db.PingContext(ctx); err != nil {\n    return fmt.Errorf(\"database unreachable before migration: %w\", err)\n}\n// ensure binary and DB schema versions are not inverted (newer DB than binary)\n// e.g. check the latest recorded version in the cursor table vs src.latest()","typeGuard":"var dbErr *database.DBError\nif errors.As(err, &dbErr) {\n    // narrow to a database-level failure: inspect dbErr.Code / Query\n    log.Printf(\"migration failed with DB code %v on %s\", dbErr.Code, dbErr.Query)\n}","tryCatchPattern":"count, err := runMigrations(ctx, db, src, min, upTo, commitEachStep)\nif err != nil {\n    var migErr interface{ Unwrap() error }\n    if errors.As(err, &migErr) {\n        log.Printf(\"migration pass aborted after %d applied: %v\", count, err)\n    }\n    // do NOT blindly retry: inspect the inner DB error and reconcile\n    // partial schema debris before re-running the pass\n    return fmt.Errorf(\"apply migrations (applied %d): %w\", count, err)\n}","preventionTips":["Never kill or Ctrl-C a running migration pass; let it finish or use the documented repair path.","Keep the `bd` binary and database versions in lockstep; don't point an old binary at a newer database.","Back up the .beads directory before upgrading.","Let `bd doctor` clean up partial state before re-running migrations.","Test upgrades on a copy of a production rig first."],"tags":["database","migration","schema","dolt"],"backgroundTag":"migration-application-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}