windmill-labs/windmill · error
Invalid migration name '${name}': use only letters, digits,
Error message
Invalid migration name '${name}': use only letters, digits, '_' and '-' What it means
`createMigration` scaffolds empty `<timestamp>_<name>.up.sql`/`.down.sql` files for a datatable migration. It validates the name against a regex allowing only letters, digits, underscore and hyphen, and throws this error for anything else (spaces, dots, slashes, etc.), preventing invalid filenames or path traversal.
Source
Thrown at cli/src/commands/datatable_migrations.ts:53
function nextMigrationTimestamp(dir: string): string {
const now = Number(migrationTimestamp());
let max = 0;
if (fs.existsSync(dir)) {
for (const file of fs.readdirSync(dir)) {
const m = file.match(/^(\d+)_.*\.(up|down)\.sql$/);
if (m) max = Math.max(max, Number(m[1]));
}
}
return String(max >= now ? max + 1 : now);
}
/**
* Scaffold a new migration under migrations/datatable/<datatable>/ as empty
* `<timestamp>_<name>.up.sql` and `.down.sql` files. Purely local — no network.
*/
export function createMigration(datatable: string, name: string): void {
if (!MIGRATION_NAME_RE.test(name)) {
throw new Error(
`Invalid migration name '${name}': use only letters, digits, '_' and '-'`,
);
}
const dir = path.join(process.cwd(), MIGRATIONS_DIR, datatable);
fs.mkdirSync(dir, { recursive: true });
const timestamp = nextMigrationTimestamp(dir);
const base = `${timestamp}_${name}`;
const up = path.join(dir, `${base}.up.sql`);
const down = path.join(dir, `${base}.down.sql`);
// Frame the body in an explicit transaction so it applies atomically, matching
// the template the UI's "New migration" modal seeds.
const template = (direction: string) =>
`-- ${direction} migration: ${name}\nBEGIN;\n\n-- Add your migration here\n\nEND;\n`;
fs.writeFileSync(up, template("up"), "utf-8");
fs.writeFileSync(down, template("down"), "utf-8");
log.info(View on GitHub (pinned to e474e8803c)
Solutions
- Rename the migration using only letters, digits, '_' and '-', e.g. `add-users-table`.
- Quote the argument in your shell to avoid word-splitting, then replace offending characters.
- Use kebab-case or snake_case names going forward.
Example fix
// before wmill datatable migrate new main add users table // after wmill datatable migrate new main add-users-table
Defensive patterns
Strategy: validation
Validate before calling
const RE = /^[A-Za-z0-9_-]+$/;
if (!RE.test(name)) throw new Error(`migration name '${name}' must match [A-Za-z0-9_-]`); Type guard
function isValidMigrationName(n: string): boolean { return /^[A-Za-z0-9_-]+$/.test(n); } Try / catch
try { createMigration(dt, name) } catch (e) { if (String(e).includes('Invalid migration name')) name = name.replace(/[^A-Za-z0-9_-]/g, '-'); } Prevention
- Use kebab-case or snake_case migration names
- Quote arguments in shell to avoid splitting
- Sanitize names derived from ticket titles/descriptions before passing
When it happens
Trigger: Running `wmill datatable migrate new <datatable> <name>` where `<name>` contains characters outside [A-Za-z0-9_-] — e.g. spaces, dots ('1.2.3'), slashes, or shell-expanded glob characters.
Common situations: Copy-pasting a description with spaces as the migration name; using dotted version numbers; accidentally passing a file path as the name.
Related errors
- Fork workspace name is too long (${effectiveName.length} cha
- Fork workspace id \`${id}\` is invalid: ${reason}. Choose a
- Unknown workspace dependencies file format: ${path}. Valid f
- Cannot push flow ${remotePath}: step(s) reference non-worksp
- Completed jobs file must contain an array of jobs
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/4601d45c40bdc482.
Report an issue: GitHub.