{"record":{"id":"865138ba2290ae35","repo":"remix-run/remix","slug":"cannot-combine-to-and-step-migration-options-i-865138","errorCode":null,"errorMessage":"Cannot combine \"to\" and \"step\" migration options in the same run","messagePattern":"Cannot combine \"to\" and \"step\" migration options in the same run","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/data-table/src/lib/migrations/runner.ts","lineNumber":58,"sourceCode":"  migrations: MigrationDescriptor[]\n  journalTable: string\n  direction: MigrationDirection\n  options: MigrationOperationOptions\n}\n\nfunction assertStepOption(step: number | undefined): void {\n  if (step === undefined) {\n    return\n  }\n\n  if (!Number.isInteger(step) || step < 1) {\n    throw new Error('Invalid migration step option. Expected a positive integer.')\n  }\n}\n\nfunction assertMigrationOperationOptions(options: MigrationOperationOptions): void {\n  if (options.to !== undefined && options.step !== undefined) {\n    throw new Error('Cannot combine \"to\" and \"step\" migration options in the same run')\n  }\n}\n\nfunction resolveTargetOption(\n  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) {","sourceCodeStart":40,"sourceCodeEnd":76,"githubUrl":"https://github.com/remix-run/remix/blob/9696913134be3a4423513d2775f7b31d6917c049/packages/data-table/src/lib/migrations/runner.ts#L40-L76","documentation":"The migration runner was given both `to` (a target migration) and `step` (a count) in the same options object. These define conflicting end conditions for the run, so the combination is rejected up front.","triggerScenarios":"Calling runMigrations with `{ to: '20240101123045_create_users', step: 3 }` — both keys defined triggers assertMigrationOperationOptions inside runMigrationsUnlocked.","commonSituations":"Spreading a defaults object containing `step` into options that also set `to`; CLI flags `--to X --step N` passed together.","solutions":["Remove one of the two options: either target an id with `to`, or a count with `step`.","If defaults set `step`, override it to `undefined` when specifying `to`: `{ step: undefined, to }`.","Add CLI flag mutual-exclusion checks before invoking the runner."],"exampleFix":"// before\nawait runMigrations(db, { direction: 'up', to, step })\n\n// after\nawait runMigrations(db, { direction: 'up', to })","handlingStrategy":"validation","validationCode":"if (options.to !== undefined && options.step !== undefined) {\n  delete options.step // or reject with a clear CLI error\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Make --to and --step mutually exclusive in your CLI flag parser.","Avoid spreading default option objects that carry `step` when `to` is set."],"tags":["migrations","options-validation","conflicting-options"],"backgroundTag":"conflicting-options","analyzedSha":"9696913134be3a4423513d2775f7b31d6917c049","analyzedAt":"2026-08-27T19:55:01.024Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}