{"record":{"id":"8788b0411776c96c","repo":"usebruno/bruno","slug":"migration-migration-name-sequence-migratio","errorCode":null,"errorMessage":"Migration \"${migration.name}\" (sequence ${migration.sequence}) does not match the migration already applied to the database. It may have been modified after being applied.","messagePattern":"Migration \"(.+?)\" \\(sequence (.+?)\\) does not match the migration already applied to the database\\. It may have been modified after being applied\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/bruno-sqlite/src/node/db.ts","lineNumber":73,"sourceCode":"  }\n\n  _applyPending(db: DatabaseSync, migrations: Migration[]): void {\n    const appliedRows = db\n      .prepare(`SELECT sequence, up_hash, down_hash FROM _migrations`)\n      .all() as { sequence: number; up_hash: string; down_hash: string }[];\n    const applied = new Map(appliedRows.map((row) => [row.sequence, row] as const));\n\n    const insertStmt = db.prepare(\n      `INSERT INTO _migrations (sequence, name, up, down, up_hash, down_hash) VALUES (?, ?, ?, ?, ?, ?)`\n    );\n    for (const migration of migrations) {\n      const upHash = this._hash(migration.up);\n      const downHash = this._hash(migration.down);\n\n      const existing = applied.get(migration.sequence);\n      if (existing !== undefined) {\n        if (existing.up_hash !== upHash || existing.down_hash !== downHash) {\n          throw new Error(\n            `Migration \"${migration.name}\" (sequence ${migration.sequence}) does not match the migration already applied to the database. It may have been modified after being applied.`\n          );\n        }\n        continue;\n      }\n\n      this._transaction(() => {\n        db.exec(migration.up);\n        insertStmt.run(\n          migration.sequence,\n          migration.name,\n          migration.up,\n          migration.down,\n          upHash,\n          downHash\n        );\n      });\n    }","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/usebruno/bruno/blob/9bdd81c7bdc57006e5f5ebffb79321a8d979f712/packages/bruno-sqlite/src/node/db.ts#L55-L91","documentation":"Thrown by _applyPending when a migration whose sequence number is already recorded in _migrations has up_hash or down_hash that differs from the current migration file's hash. The library hashes the up and down SQL with sha256 and stores both, so any post-apply edit to a migration's SQL is detected and rejected to preserve database/migration history integrity. There is no automatic recovery; the database is left as-is and the constructor closes the handle.","triggerScenarios":"Developer edits an already-shipped migration's up or down SQL; a merge conflict resolution changes SQL text in an old migration; the migrations array is reordered such that the same sequence now maps to different content; running against a database from an older app version whose current build's migration content differs.","commonSituations":"Editing an existing migration instead of adding a new one to fix a bug; cherry-picking commits that touched migration SQL; copy-paste error producing whitespace-only diffs that still change the hash; CI running against a dev DB that was migrated by a different branch.","solutions":["Do NOT edit the offending migration. Add a NEW migration (incremented sequence) that performs the corrective DDL.","If you intentionally must rewrite history (dev-only): roll back the migration (run its down via _rollbackObsolete or drop the _migrations row for that sequence) and re-apply — only safe on a DB you can fully recreate.","If a merge introduced the drift, restore the original SQL of the applied migration and add a fresh migration for the new intent.","If the DB itself is stale/corrupt from a different branch, delete the SQLite file and let migrations run clean from zero."],"exampleFix":"// before — editing an applied migration's `up`\nexport const myMigration = { sequence: 3, name: 'add_users', up: 'CREATE TABLE users (...); ALTER TABLE users ADD col x;', down: '...' };\n\n// after — leave sequence 3 untouched, add a new migration\nexport const addUsersCol = { sequence: 4, name: 'add_users_col_x', up: 'ALTER TABLE users ADD COLUMN x;', down: 'ALTER TABLE users DROP COLUMN x;' };","handlingStrategy":"validation","validationCode":"import { createHash } from 'node:crypto';\n// Before opening the DB, fail fast if any migration drifted vs. what's applied.\nfunction assertMigrationsConsistent(appliedRows, migrations) {\n  const bySeq = new Map(appliedRows.map(r => [r.sequence, r]));\n  for (const m of migrations) {\n    const a = bySeq.get(m.sequence);\n    if (!a) continue;\n    const up = createHash('sha256').update(m.up).digest('hex');\n    const down = createHash('sha256').update(m.down).digest('hex');\n    if (a.up_hash !== up || a.down_hash !== down) {\n      throw new Error(`Migration '${m.name}' seq=${m.sequence} drifted. Restore original SQL or add a new migration.`);\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  return createDatabase(dbPath, options);\n} catch (e) {\n  if (/does not match the migration already applied/.test(e?.message)) {\n    // hard-stop with actionable guidance — do not auto-rewrite history\n    throw new Error('DB migration drift detected. Add a NEW migration; do not edit applied ones. Detail: ' + e.message);\n  }\n  throw e;\n}","preventionTips":["Treat migrations as immutable once shipped; corrections always go in a new sequence.","Code-review migration diffs for edits to existing sequence numbers.","Run a checksum/drift check in CI against the migrations bundle.","Keep a dev DB reset script so dev DBs are recreatable when history legitimately changes."],"tags":["database","migration","sqlite","data-integrity","schema"],"backgroundTag":null,"analyzedSha":"9bdd81c7bdc57006e5f5ebffb79321a8d979f712","analyzedAt":"2026-08-13T04:09:25.751Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}