remix-run/remix · error · Error

Duplicate migration id: {migration.id}

Error message

Duplicate migration id: {migration.id}

What it means

createMigrationRegistry was initialized with two MigrationDescriptor entries sharing the same `id`. Since ids drive ordering and journal bookkeeping, duplicates are rejected eagerly at registry construction.

Source

Thrown at packages/data-table/src/lib/migrations/registry.ts:44

/**
 * Creates an in-memory migration registry.
 * @param initial Optional initial migration list.
 * @returns A migration registry with duplicate-id protection.
 * @example
 * ```ts
 * import { createMigrationRegistry } from 'remix/data-table/migrations'
 *
 * let registry = createMigrationRegistry()
 * registry.register({ id, name, up, down })
 * ```
 */
export function createMigrationRegistry(initial: MigrationDescriptor[] = []): MigrationRegistry {
  let migrations = new Map<string, MigrationDescriptor>()

  for (let migration of initial) {
    if (migrations.has(migration.id)) {
      throw new Error('Duplicate migration id: ' + migration.id)
    }

    migrations.set(migration.id, migration)
  }

  return {
    register(migration: MigrationDescriptor) {
      if (migrations.has(migration.id)) {
        throw new Error('Duplicate migration id: ' + migration.id)
      }

      migrations.set(migration.id, migration)
    },
    list() {
      return sortMigrations(Array.from(migrations.values()))
    },
  }
}

View on GitHub (pinned to 9696913134)

Solutions

  1. Inspect the descriptors passed as `initial` and remove or rename the duplicate id.
  2. Check for the same migration directory being scanned twice by your loader.
  3. If two distinct migrations share a timestamp-second id, regenerate one with a later timestamp and update the journal.

Example fix

// before
createMigrationRegistry([
  { id: '20240101123045', name: 'create_users', ... },
  { id: '20240101123045', name: 'add_index', ... },
])

// after
createMigrationRegistry([
  { id: '20240101123045', name: 'create_users', ... },
  { id: '20240101123046', name: 'add_index', ... },
])
Defensive patterns

Strategy: validation

Validate before calling

let ids = descriptors.map((m) => m.id)
if (new Set(ids).size !== ids.length) {
  let dup = ids.find((id, i) => ids.indexOf(id) !== i)
  throw new Error('duplicate migration id in input: ' + dup)
}

Type guard

function hasUniqueIds(migrations: MigrationDescriptor[]): boolean {
  return new Set(migrations.map((m) => m.id)).size === migrations.length
}

Try / catch

try {
  let registry = createMigrationRegistry(all)
} catch (error) {
  if (error instanceof Error && error.message.startsWith('Duplicate migration id')) {
    // dedupe by id, keeping first, then retry
  }
  throw error
}

Prevention

When it happens

Trigger: Passing an `initial` array (e.g. loaded module descriptors) to createMigrationRegistry where two entries have the same `id` string, often from loading the same directory twice or two migrations generated in the same second.

Common situations: Globbing migrations from overlapping directories; a rebase duplicating a migration file; two migrations created within the same timestamp second.

Related errors


AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27). Data as JSON: /api/errors/fd9a49591389fd6f. Report an issue: GitHub.