usebruno/bruno · critical · Error

Migration "${migration.name}" (sequence ${migration.sequence

Error message

Migration "${migration.name}" (sequence ${migration.sequence}) does not match the migration already applied to the database. It may have been modified after being applied.

What it means

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.

Source

Thrown at packages/bruno-sqlite/src/node/db.ts:73

  }

  _applyPending(db: DatabaseSync, migrations: Migration[]): void {
    const appliedRows = db
      .prepare(`SELECT sequence, up_hash, down_hash FROM _migrations`)
      .all() as { sequence: number; up_hash: string; down_hash: string }[];
    const applied = new Map(appliedRows.map((row) => [row.sequence, row] as const));

    const insertStmt = db.prepare(
      `INSERT INTO _migrations (sequence, name, up, down, up_hash, down_hash) VALUES (?, ?, ?, ?, ?, ?)`
    );
    for (const migration of migrations) {
      const upHash = this._hash(migration.up);
      const downHash = this._hash(migration.down);

      const existing = applied.get(migration.sequence);
      if (existing !== undefined) {
        if (existing.up_hash !== upHash || existing.down_hash !== downHash) {
          throw new Error(
            `Migration "${migration.name}" (sequence ${migration.sequence}) does not match the migration already applied to the database. It may have been modified after being applied.`
          );
        }
        continue;
      }

      this._transaction(() => {
        db.exec(migration.up);
        insertStmt.run(
          migration.sequence,
          migration.name,
          migration.up,
          migration.down,
          upHash,
          downHash
        );
      });
    }

View on GitHub (pinned to 9bdd81c7bd)

Solutions

  1. Do NOT edit the offending migration. Add a NEW migration (incremented sequence) that performs the corrective DDL.
  2. 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.
  3. If a merge introduced the drift, restore the original SQL of the applied migration and add a fresh migration for the new intent.
  4. If the DB itself is stale/corrupt from a different branch, delete the SQLite file and let migrations run clean from zero.

Example fix

// before — editing an applied migration's `up`
export const myMigration = { sequence: 3, name: 'add_users', up: 'CREATE TABLE users (...); ALTER TABLE users ADD col x;', down: '...' };

// after — leave sequence 3 untouched, add a new migration
export const addUsersCol = { sequence: 4, name: 'add_users_col_x', up: 'ALTER TABLE users ADD COLUMN x;', down: 'ALTER TABLE users DROP COLUMN x;' };
Defensive patterns

Strategy: validation

Validate before calling

import { createHash } from 'node:crypto';
// Before opening the DB, fail fast if any migration drifted vs. what's applied.
function assertMigrationsConsistent(appliedRows, migrations) {
  const bySeq = new Map(appliedRows.map(r => [r.sequence, r]));
  for (const m of migrations) {
    const a = bySeq.get(m.sequence);
    if (!a) continue;
    const up = createHash('sha256').update(m.up).digest('hex');
    const down = createHash('sha256').update(m.down).digest('hex');
    if (a.up_hash !== up || a.down_hash !== down) {
      throw new Error(`Migration '${m.name}' seq=${m.sequence} drifted. Restore original SQL or add a new migration.`);
    }
  }
}

Try / catch

try {
  return createDatabase(dbPath, options);
} catch (e) {
  if (/does not match the migration already applied/.test(e?.message)) {
    // hard-stop with actionable guidance — do not auto-rewrite history
    throw new Error('DB migration drift detected. Add a NEW migration; do not edit applied ones. Detail: ' + e.message);
  }
  throw e;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of usebruno/bruno@9bdd81c7bd (2026-08-13). Data as JSON: /api/errors/8788b0411776c96c. Report an issue: GitHub.