remix-run/remix · critical · Error
Migration checksum drift detected for "{row.id}" (journal={r
Error message
Migration checksum drift detected for "{row.id}" (journal={row.checksum}, current={expected}) What it means
A journal row's stored checksum no longer matches the checksum computed from the current migration file. This means an already-applied migration was edited after being applied, so the journal history and the source of truth have diverged; the runner halts the forward run.
Source
Thrown at packages/data-table/src/lib/migrations/runner.ts:117
for (let row of journal) {
let migration = migrationMap.get(row.id)
if (!migration) {
// Rolling back must stay possible when journal rows have no matching
// migration files, so only forward runs hard-error on orphaned entries.
if (direction === 'down') {
continue
}
throw new Error(
'Applied migration "' + row.id + '_' + row.name + '" is missing from current migrations',
)
}
let expected = await computeChecksum(migration)
if (expected !== row.checksum) {
throw new Error(
'Migration checksum drift detected for "' +
row.id +
'" (journal=' +
row.checksum +
', current=' +
expected +
')',
)
}
}
}
function resolveTransactionMode(migration: MigrationDescriptor): MigrationTransactionMode {
if (migration.transaction) {
return migration.transaction
}
let directive = parseTransactionDirective(migration.up)View on GitHub (pinned to 9696913134)
Solutions
- Revert the edited migration file to its applied content (check git history for the original).
- If the edit was intentional, recreate the environment: roll back the migration and re-apply, or drop/rebuild the database in dev.
- As a last resort, update the journal row's checksum to the new computed value — only when you are certain the change is safe and all environments get the same file.
- Exclude migration files from formatters and normalize line endings (.gitattributes `* text=auto eol=lf`).
Example fix
# before: edited applied migration in place # 20240101123045_create_users/migration.sql (modified) # after: restore original git checkout <last-applied-commit> -- path/to/migration.sql # or (dev only) rebuild from scratch: # dropdb && recreatedb && runMigrations up
Defensive patterns
Strategy: try-catch
Validate before calling
for (let row of await db.select().from(journalTable)) {
let migration = registry.list().find((m) => m.id === row.id)
if (migration && (await computeChecksum(migration)) !== row.checksum) {
throw new Error(`file for ${row.id} has drifted from the applied version`)
}
} Try / catch
try {
await runMigrations(db, { direction: 'up' })
} catch (error) {
if (error instanceof Error && error.message.includes('checksum drift')) {
// diff the file vs git history; restore original or rebuild the database
}
throw error
} Prevention
- Treat applied migrations as immutable once committed.
- Exclude migration files from formatters and enforce LF line endings via .gitattributes.
- Run a checksum verification step in CI against a scratch database.
When it happens
Trigger: Editing a migration file's SQL/content after it was applied and then running `direction: 'up'`; reformatting or line-ending changes that alter the bytes hashed by computeChecksum during assertMigrationIntegrity.
Common situations: Fixing a typo directly in an old migration; a formatter/linter rewriting migration files on save or in CI; inconsistent line endings (CRLF vs LF) across machines.
Related errors
- Applied migration "{row.id}_{row.name}" is missing from curr
- expected promise to resolve, but it rejected with: ${stringi
- ${optionName} values must be package names. Received "${pack
- Cannot combine "to" and "step" migration options in the same
- Duplicate migration id "{id}" inferred from directory "{dire
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/3feeec803c8b5ed9.
Report an issue: GitHub.