remix-run/remix · error · Error
Invalid migration step option. Expected a positive integer.
Error message
Invalid migration step option. Expected a positive integer.
What it means
The `step` option passed to the migration runner was defined but not a positive integer. Steps control how many migrations to apply/rollback, so fractional, zero, negative, or non-integer values are rejected before any work runs.
Source
Thrown at packages/data-table/src/lib/migrations/runner.ts:52
import { resolveMigrations } from './registry.ts'
type MigrationRunnerContext = DatabaseDriver
type RunMigrationsInput = {
driver: MigrationRunnerContext
migrations: MigrationDescriptor[]
journalTable: string
direction: MigrationDirection
options: MigrationOperationOptions
}
function assertStepOption(step: number | undefined): void {
if (step === undefined) {
return
}
if (!Number.isInteger(step) || step < 1) {
throw new Error('Invalid migration step option. Expected a positive integer.')
}
}
function assertMigrationOperationOptions(options: MigrationOperationOptions): void {
if (options.to !== undefined && options.step !== undefined) {
throw new Error('Cannot combine "to" and "step" migration options in the same run')
}
}
function resolveTargetOption(
migrations: MigrationDescriptor[],
to: string | undefined,
): string | undefined {
if (to === undefined) {
return undefined
}
// Accept either a bare migration id or the full `id_name` directory form andView on GitHub (pinned to 9696913134)
Solutions
- Pass a positive integer: `step: 2` for two migrations, or omit `step` entirely.
- Coerce CLI/env inputs with `Number.parseInt(value, 10)` and validate before calling run.
- Use `1` for single-step rollbacks instead of `0` or `-1`.
Example fix
// before
await runMigrations(db, { direction: 'down', step: Number(argv[2]) })
// after
await runMigrations(db, { direction: 'down', step: Math.max(1, Number.parseInt(argv[2], 10) || 1) }) Defensive patterns
Strategy: type-guard
Validate before calling
if (step !== undefined && (!Number.isInteger(step) || step < 1)) {
throw new Error('--step must be a positive integer')
} Type guard
function isValidStep(step: unknown): step is number {
return typeof step === 'number' && Number.isInteger(step) && step >= 1
} Prevention
- Parse and validate CLI args before passing them into migration options.
- Clamp computed step values to at least 1.
When it happens
Trigger: Calling runMigrations with `{ step: 0 }`, `{ step: -1 }`, `{ step: 1.5 }`, or `{ step: '2' }` (string from CLI/env not coerced). assertStepOption is invoked by runMigrationsUnlocked.
Common situations: Passing a raw CLI argument or environment variable straight into options without parsing; computing step as a difference that can be 0 or negative.
Understand the failure class
Background: Invalid option value errors: "must be one of", "is not a valid", and "only allows" failures explained — this error's family across 23 libraries.
Related errors
- 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
- Cannot combine "to" and "step" migration options in the same
- Unknown migration target: {to}
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/7857b5ee0bcf89a9.
Report an issue: GitHub.