diesel-rs/diesel · error
Invalid migration directory: the directory's name should be
Error message
Invalid migration directory: the directory's name should be <timestamp>_<name_of_migration>, and it should contain up.sql and optionally down.sql.
What it means
Raised by `migration_literal_from_path` when a directory inside the migrations folder does not have a name starting with a timestamp/version (`<timestamp>_<name>`). The `embed_migrations!` macro parses each child directory name with `version_from_string`; if no numeric version prefix is found it panics with this message at compile time.
Solutions
- Rename the offending directory to `<timestamp>_<name>` (timestamp ascending, e.g. `20230901120000_fix_users`).
- Remove stray non-migration directories/files from the migrations folder.
- Use `diesel migration generate <name>` so naming is always valid.
- Check `cargo tree`/diesel version: very old timestamps (e.g. date-based) vs numeric schemas can matter; align names with your diesel version's expected format.
Example fix
// before migrations/fix_users/up.sql // after migrations/20230901120000_fix_users/up.sql
Defensive patterns
Strategy: validation
Validate before calling
use std::fs;
for e in fs::read_dir("migrations").unwrap() {
let name = e.unwrap().file_name().to_string_lossy().into_owned();
let Some((version, _)) = name.split_once('_') else {
panic!("bad migration dir: {name}")
};
assert!(!version.is_empty() && version.chars().all(|c| c.is_ascii_digit()), "bad version in {name}");
} Type guard
fn valid_migration_name(name: &str) -> bool {
match name.split_once('_') {
Some((v, rest)) => !v.is_empty() && v.chars().all(|c| c.is_ascii_digit()) && !rest.is_empty(),
None => false,
}
} Prevention
- Use `diesel migration generate <name>` for every migration.
- Never rename away the `<timestamp>_` prefix.
- Keep the migrations dir free of stray folders (CI check with the validator above).
When it happens
Trigger: A subdirectory of the embedded migrations path whose name does not start with `<timestamp>_` (e.g. `fix_users`, `temp`, or a name where the part before `_` is not parseable as a version number).
Common situations: Hand-creating migration folders with casual names; renaming a migration and accidentally removing the timestamp prefix; checkout leaving stray directories (`.git`, `node_modules`, editor dirs) inside `migrations/`; non-diesel migration folders copied in.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- Can't get file name from path
- Failed to create embedded migrations instance
- ` ` must be in the form `#[ ="something"]
- expected a integer literal expression, but got something…
- no `#[diesel(sql_type = ...)]` attribute provided
AI-assisted analysis of diesel-rs/diesel@6fa6ed01b2 (2026-09-07).
Data as JSON: /api/errors/8e86818a1959f930.
Report an issue: GitHub.
Appendix: source
Thrown at diesel_migrations/migrations_macros/src/embed_migrations.rs:45
path: &Path,
) -> Result<Vec<proc_macro2::TokenStream>, Box<dyn Error>> {
let mut migrations = migrations_directories(path)?.collect::<Result<Vec<_>, _>>()?;
migrations.sort_by_key(DirEntry::path);
Ok(migrations
.into_iter()
.map(|e| migration_literal_from_path(&e.path()))
.collect())
}
fn migration_literal_from_path(path: &Path) -> proc_macro2::TokenStream {
let name = path
.file_name()
.unwrap_or_else(|| panic!("Can't get file name from path `{path:?}`"))
.to_string_lossy();
if version_from_string(&name).is_none() {
panic!(
"Invalid migration directory: the directory's name should be \
<timestamp>_<name_of_migration>, and it should contain \
up.sql and optionally down.sql."
);
}
let up_sql_path = path.join("up.sql");
let up_sql_path = up_sql_path.to_str();
let down_sql_path = path.join("down.sql");
let metadata = TomlMetadata::read_from_file(&path.join("metadata.toml")).unwrap_or_default();
let run_in_transaction = metadata.run_in_transaction;
let down_sql = match down_sql_path.metadata() {
Err(e) if e.kind() == std::io::ErrorKind::NotFound => quote! { None },
_ => {
let down_sql_path = down_sql_path.to_str();
quote! { Some(include_str!(#down_sql_path)) }
}
};View on GitHub (pinned to 6fa6ed01b2)