Skip to content

db push fails: 'Remote migration versions not found' when 8-digit and 14-digit migration timestamps share the same prefix #6036

Description

Summary

supabase db push fails with "Remote migration versions not found in local migrations directory" when local migration files mix 8-digit and 14-digit version timestamps that share the same numeric prefix.

Environment

  • Supabase CLI: 2.105.0 (Go implementation, apps/cli-go)
  • OS: Windows

Minimal reproduction

Local supabase/migrations/ directory containing:

20260420_foo.sql
20260420010000_bar.sql

Remote supabase_migrations.schema_migrations containing both versions:

20260420
20260420010000

Running supabase db push --dry-run fails:

Remote migration versions not found in local migrations directory.

Make sure your local git repo is up-to-date. If the error persists, try repairing the migration history table:
supabase migration repair --status reverted 20260420

Expected behavior

Since both 20260420 and 20260420010000 exist locally and remotely, the CLI should recognize them as applied and report the remote database as up to date (or push only genuinely pending migrations). migration repair --status reverted 20260420 should not be suggested, because the version is present locally.

Actual behavior

The CLI reports version 20260420 as missing locally and suggests reverting it. Removing the row via repair --status reverted does not help — the same versions are then reported as unapplied local files instead (ErrMissingRemote), and there is no way to reconcile via db push.

Root cause

FindPendingMigrations (in apps/cli-go/pkg/migration/apply.go) merges two lists assuming both are in the same order:

  • Remote list: built from SELECT version FROM supabase_migrations.schema_migrations ORDER BY version (pkg/migration/list.go) — pure lexicographic string ordering of versions. With ORDER BY version, 20260420 < 20260420010000 (prefix string is smaller).

  • Local list: built via fs.ReadDir in ListLocalMigrations (pkg/migration/list.go), which sorts by full file name. 20260420010000_bar.sql sorts before 20260420_foo.sql because the 9th character '0' (0x30) is less than '_' (0x5F).

So the two lists disagree on relative order. In the merge loop:

if remote == local { j++; i++ }
else if remote < local { missing = append(missing, remote); i++ }
else { unapplied = append(unapplied, localMigrations[j]); j++ }

When remote = "20260420" is compared against the first local file 20260420010000_bar.sql (version 20260420010000), the branch remote < local marks 20260420 as missing. By the time the merge reaches the actual 20260420_foo.sql file, the remote cursor has already advanced past 20260420, so it is classified as unapplied instead of matching. The version ends up permanently "missing" and ErrMissingLocal is returned.

Impact

Projects that historically created migrations with 8-digit timestamps (as the legacy TypeScript CLI accepted) and later added 14-digit migrations sharing the same numeric prefix can never run db push again, because the ordering inversion produces a false "missing local migration" error for every affected 8-digit version.

Affected pattern (8-digit version + any 14-digit version with the same 8-digit prefix):

  • 20260420 + 20260420010000
  • 20260428 + 20260428010000
  • 20260501 + 20260501010000
  • 20260502 + 20260502010000

Suggested fix

In FindPendingMigrations (or in ListLocalMigrations), order the local migrations by their extracted version (migrateFilePattern group 1) instead of by raw file name, so both lists share a consistent ordering and the merge stays aligned. Alternatively, compare using the extracted version number in both sides and tolerate out-of-order file names.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions