remix-run/remix · error · Error
Duplicate migration id "{id}" inferred from directory "{dire
Error message
Duplicate migration id "{id}" inferred from directory "{directoryName}" What it means
Migrations are loaded from timestamped directories (e.g. 20240101000000_add_users), and the leading id must be unique. If two directories parse to the same migration id, an Error is thrown naming the duplicate and the offending directory, because ordering and applied-state tracking would become ambiguous.
Source
Thrown at packages/data-table/src/lib/migrations-node.ts:38
*
* let migrations = await loadMigrations('./app/db/migrations')
* ```
*/
export async function loadMigrations(directory: string): Promise<MigrationDescriptor[]> {
let entries = await fs.readdir(directory, { withFileTypes: true })
let directories = entries
.filter((entry) => entry.isDirectory())
.map((entry) => entry.name)
.sort((left, right) => left.localeCompare(right))
let migrations: MigrationDescriptor[] = []
let seenIds = new Set<string>()
for (let directoryName of directories) {
let parsed = parseMigrationDirectoryName(directoryName)
if (seenIds.has(parsed.id)) {
throw new Error(
'Duplicate migration id "' +
parsed.id +
'" inferred from directory "' +
directoryName +
'"',
)
}
seenIds.add(parsed.id)
let directoryPath = path.join(directory, directoryName)
let upPath = path.join(directoryPath, 'up.sql')
let downPath = path.join(directoryPath, 'down.sql')
let up: string
try {
up = await fs.readFile(upPath, 'utf8')
} catch (error) {View on GitHub (pinned to 9696913134)
Solutions
- Rename one of the conflicting directories so its leading id is unique (bump the timestamp or add a suffix per the naming convention)
- If two migrations from merged branches collide, re-timestamp the newer one before running migrations
- Adopt unique, monotonic ids in your migration generator (date + sequence)
Example fix
# before migrations/ 20240101000000_add_users/ 20240101000000_add_posts/ # duplicate id # after migrations/ 20240101000000_add_users/ 20240101000100_add_posts/
Defensive patterns
Strategy: validation
Validate before calling
let ids = directories.map(d => parseMigrationDirectoryName(d).id)
let dupes = ids.filter((id, i) => ids.indexOf(id) !== i)
if (dupes.length) throw new Error('Duplicate migration ids: ' + dupes.join(', ')) Type guard
function hasUniqueMigrationIds(directories: string[]): boolean {
let ids = directories.map((d) => d.split('_')[0])
return new Set(ids).size === ids.length
} Prevention
- Generate migration ids with timestamp + sequence to guarantee uniqueness
- After merging branches, scan the migrations directory for duplicate prefixes
- Never copy a migration directory without renaming its timestamp
When it happens
Trigger: Two migration directories with the same timestamp prefix (copying a directory to rename it and keeping the id); generating migrations in the same second without a sequence suffix; case-insensitive filesystems treating different names as one.
Common situations: Copying an existing migration folder as a starting point and forgetting to change its timestamp; two developers/branches generating migrations with identical timestamps that collide after merge; automated generation without a monotonic clock.
Related errors
- Migration directory "{directoryName}" is missing up.sql
- Duplicate migration id: {migration.id}
- expected promise to resolve, but it rejected with: ${stringi
- ${optionName} values must be package names. Received "${pack
- Resolved path escapes the allowed root: ${relativePath}
AI-assisted analysis of remix-run/remix@9696913134 (2026-08-27).
Data as JSON: /api/errors/2c1ae460b64db45a.
Report an issue: GitHub.