{"record":{"id":"403b833db507276f","repo":"remix-run/remix","slug":"ambiguous-migration-target-to-matches-match","errorCode":null,"errorMessage":"Ambiguous migration target \"{to}\". Matches: {matches}","messagePattern":"Ambiguous migration target \"(.+?)\"\\. Matches: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/data-table/src/lib/migrations/runner.ts","lineNumber":81,"sourceCode":"  migrations: MigrationDescriptor[],\n  to: string | undefined,\n): string | undefined {\n  if (to === undefined) {\n    return undefined\n  }\n\n  // Accept either a bare migration id or the full `id_name` directory form and\n  // normalize to the bare id so range filtering compares ids consistently.\n  let matches = migrations.filter(\n    (migration) => migration.id === to || migration.id + '_' + migration.name === to,\n  )\n\n  if (matches.length === 0) {\n    throw new Error('Unknown migration target: ' + to)\n  }\n\n  if (matches.length > 1) {\n    throw new Error(\n      'Ambiguous migration target \"' +\n        to +\n        '\". Matches: ' +\n        matches.map((migration) => migration.id + '_' + migration.name).join(', '),\n    )\n  }\n\n  return matches[0].id\n}\n\nasync function assertMigrationIntegrity(\n  migrations: MigrationDescriptor[],\n  journal: MigrationJournalRow[],\n  direction: MigrationDirection,\n): Promise<void> {\n  let migrationMap = new Map(migrations.map((migration) => [migration.id, migration]))\n\n  for (let row of journal) {","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/data-table/src/lib/migrations/runner.ts#L63-L99","documentation":"The `to` target matched more than one registered migration. This happens when one migration's bare id equals another's full `id_name` string — both comparisons succeed, so the runner refuses to guess and reports all matches.","triggerScenarios":"A migration named such that its full `id_name` string collides with another migration's bare numeric id, e.g. migration A has id `X` and name `Y_Z`, and migration B has id `X_Y_Z`. Then `to: 'X'` or the colliding string matches both.","commonSituations":"Migration names that begin with another migration's id-like segment; unusual hand-written ids/names that alias each other.","solutions":["Rename one of the colliding migrations so no bare id equals another's `id_name` string.","As a workaround, pass a target string that is unique among registered migrations.","Audit `registry.list()` for id/name aliasing before targeting."],"exampleFix":"// before\n// migration A: id '20240101123045', name '42_add_index'\n// migration B: id '20240101123045_42', name 'add_index'\nawait runMigrations(db, { to: '20240101123045' })\n\n// after\n// rename A to name 'add_users_index' (no aliasing)\nawait runMigrations(db, { to: '20240101123045' })","handlingStrategy":"validation","validationCode":"let matches = registry\n  .list()\n  .filter((m) => m.id === to || m.id + '_' + m.name === to)\nif (matches.length > 1) throw new Error('ambiguous target, pass a unique string')","typeGuard":"function isUnambiguousTarget(registry: MigrationRegistry, to: string): boolean {\n  return (\n    registry.list().filter((m) => m.id === to || `${m.id}_${m.name}` === to).length === 1\n  )\n}","tryCatchPattern":"try {\n  await runMigrations(db, { direction: 'up', to })\n} catch (error) {\n  if (error instanceof Error && error.message.startsWith('Ambiguous migration target')) {\n    // error message lists all matches; pick one and re-run\n  }\n  throw error\n}","preventionTips":["Avoid migration names that embed id-like numeric prefixes.","CI-check the migration set for id/name aliasing collisions."],"tags":["migrations","ambiguous-target","naming"],"backgroundTag":"ambiguous-target","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}