{"record":{"id":"6acaae8002202f2c","repo":"toeverything/AFFiNE","slug":"invalid-migration-name-migration-name","errorCode":null,"errorMessage":"Invalid migration name: ${migration.name}","messagePattern":"Invalid migration name: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/backend/server/src/data/commands/run.ts","lineNumber":21,"sourceCode":"import { PrismaClient } from '@prisma/client';\nimport { once } from 'lodash-es';\n\nimport * as migrationImports from '../migrations';\n\ninterface Migration {\n  name: string;\n  always?: boolean;\n  up: (db: PrismaClient, injector: ModuleRef) => Promise<void>;\n  down: (db: PrismaClient, injector: ModuleRef) => Promise<void>;\n  order: number;\n}\n\nexport const collectMigrations = once(() => {\n  const migrations = Object.values(migrationImports).map(migration => {\n    const order = Number(migration.name.match(/([\\d]+)$/)?.[1]);\n\n    if (Number.isNaN(order)) {\n      throw new Error(`Invalid migration name: ${migration.name}`);\n    }\n\n    return {\n      name: migration.name,\n      // @ts-expect-error optional\n      always: migration.always,\n      up: migration.up,\n      down: migration.down,\n      order,\n    };\n  }) as Migration[];\n\n  return migrations.sort((a, b) => a.order - b.order);\n});\n\n@Injectable()\nexport class RunCommand {\n  logger = new Logger(RunCommand.name);","sourceCodeStart":3,"sourceCodeEnd":39,"githubUrl":"https://github.com/toeverything/AFFiNE/blob/26c515e050211269e911f7d9cfe162a26c83ed98/packages/backend/server/src/data/commands/run.ts#L3-L39","documentation":"Thrown by `collectMigrations` when a migration module's `name` does not end in a run of digits (regex `/([\\d]+)$/`). The trailing number is parsed as the migration's run `order`; without it, `Number(undefined)` yields `NaN` and the loader aborts. A plain `Error` (no error code), thrown during module-load/CLI bootstrap.","triggerScenarios":"Adding a migration file whose name lacks a trailing numeric suffix — e.g. `add-indexes.ts` instead of `1766000000000-add-indexes.ts`. The loader iterates every exported migration module at startup and fails on the first malformed name.","commonSituations":"New migration added without following the `timestamp-slug.ts` naming convention; renaming a migration file and dropping the numeric prefix; migration file generated by a scaffold that did not append the timestamp.","solutions":["Rename the offending migration file so it ends in digits — conventionally a millisecond timestamp prefix, e.g. `1766000000000-my-migration.ts`.","Match the existing naming pattern in `packages/backend/server/src/data/migrations/` (all are `<digits>-slug.ts`).","Re-run the migration command after renaming to confirm `collectMigrations` no longer throws.","Update any scaffolding/generator template so new migrations always get a numeric suffix."],"exampleFix":"// before: src/data/migrations/add-indexes.ts\n//   (loader throws: Invalid migration name: add-indexes)\n\n// after: rename to\n//   src/data/migrations/1766000000000-add-indexes.ts","handlingStrategy":"validation","validationCode":"// Enforce naming convention in a generator/scaffold\nfunction migrationFilename(slug) {\n  const ts = Date.now();\n  return `${ts}-${slug}.ts`; // always ends in digits before .ts\n}","typeGuard":"function hasNumericSuffix(name) {\n  return /\\d+$/.test(name);\n}","tryCatchPattern":null,"preventionTips":["Always name migration files with a trailing numeric suffix (timestamp convention).","Follow the existing `<digits>-slug.ts` pattern in data/migrations.","Lint migration filenames in CI to catch malformed names before runtime."],"tags":["cli","migration","naming-convention","bootstrap","nestjs"],"backgroundTag":null,"analyzedSha":"26c515e050211269e911f7d9cfe162a26c83ed98","analyzedAt":"2026-08-12T13:15:16.447Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}