rust-lang/rust · error
Path component {:?} of path {} is an invalid filename
Error message
Path component {:?} of path {} is an invalid filename What it means
Panics inside split_path_dir_and_file while generating .debug_line info when the last component of a source path is not a Component::Normal (i.e. it is RootDir, CurDir, ParentDir, or a Windows Prefix). gimli's LineProgram requires an ordinary file-name OsStr, so an unusual path aborts debuginfo emission.
Source
Thrown at compiler/rustc_codegen_cranelift/src/debuginfo/line_info.rs:24
use cranelift_codegen::MachSrcLoc;
use cranelift_codegen::binemit::CodeOffset;
use gimli::write::{FileId, FileInfo, LineProgram, LineString, LineStringTable};
use rustc_span::{
FileName, Pos, RemapPathScopeComponents, SourceFile, SourceFileAndLine,
SourceFileHashAlgorithm, hygiene,
};
use crate::debuginfo::FunctionDebugContext;
use crate::debuginfo::emit::address_for_func;
use crate::prelude::*;
// OPTIMIZATION: It is cheaper to do this in one pass than using `.parent()` and `.file_name()`.
fn split_path_dir_and_file(path: &Path) -> (&Path, &OsStr) {
let mut iter = path.components();
let file_name = match iter.next_back() {
Some(Component::Normal(p)) => p,
component => {
panic!(
"Path component {:?} of path {} is an invalid filename",
component,
path.display()
);
}
};
let parent = iter.as_path();
(parent, file_name)
}
// OPTIMIZATION: Avoid UTF-8 validation on UNIX.
fn osstr_as_utf8_bytes(path: &OsStr) -> &[u8] {
#[cfg(unix)]
{
use std::os::unix::ffi::OsStrExt;
path.as_bytes()
}
#[cfg(not(unix))]View on GitHub (pinned to 22057b88b0)
Solutions
- Inspect the failing path printed in the panic (the `{}` is path.display()) and identify which crate/file produces it.
- Audit --remap-path-prefix / RUSTFLAGS remapping rules to ensure no rule maps a source root to `/`, `.`, or an empty string.
- Reproduce with `--remap-path-prefix=<bad>=<good>` inverted to find the offending rule, then fix the rule.
- If the path comes from a generated/build-script file, ensure the generator emits a real filename rather than a bare root or sentinel.
Example fix
// before
let file_name = match iter.next_back() {
Some(Component::Normal(p)) => p,
component => {
panic!("Path component {:?} of path {} is an invalid filename", component, path.display());
}
};
// after
let file_name = match iter.next_back() {
Some(Component::Normal(p)) => p,
Some(other) => {
// Fall back to the OsStr of whatever component we got so debuginfo can still emit.
eprintln!("cg_clif: path component {:?} of {} is not Normal; using as-is", other, path.display());
other.as_os_str()
}
None => panic!("Path {} has no components", path.display()),
}; Defensive patterns
Strategy: validation
Validate before calling
use std::path::{Path, Component};
fn validate_path(p: &Path) -> Result<(), String> {
for c in p.components() {
if let Component::Normal(os_str) = c {
if os_str.to_str().is_none() {
return Err(format!("path component {:?} in {} is not valid Unicode / contains illegal chars", os_str, p.display()));
}
let s = os_str.to_str().unwrap();
if s.is_empty() || s.contains('\0') {
return Err(format!("path component {:?} is empty or contains NUL", s));
}
#[cfg(windows)]
for bad in ['<', '>', ':', '"', '/', '\\', '|', '?', '*'] {
if s.contains(bad) { return Err(format!("component {:?} has illegal char {:?}", s, bad)); }
}
}
}
Ok(())
} Type guard
use std::path::Path;
fn path_components_are_valid_filenames(p: &Path) -> bool {
p.components().all(|c| match c {
std::path::Component::Normal(os) => os.to_str().map(|s| !s.is_empty() && !s.contains('\0')).unwrap_or(false),
_ => true,
})
} Prevention
- Keep all source / output paths pure UTF-8 with no NUL bytes; cg_clif emits DWARF using these names.
- Avoid path components with OS-illegal characters (<>:\"/\\|?* on Windows).
- Do not build inside directories whose names were created from raw bytes or non-Unicode locales.
- Use stable, ASCII-friendly crate names and target paths to keep debuginfo filename emission safe.
When it happens
Trigger: Reached during debuginfo line-program construction (line_info.rs ~107 calls split_path_dir_and_file on each source file path). The panic fires for any source file whose path, after remapping, ends in something that is not a normal filename component.
Common situations: A --remap-path-prefix mapping collapses a path to `/` or `.` so the final component becomes RootDir/CurDir; proc-macro or macro-expanded spans synthesize a virtual filename like `<anon>` or `<macro>` mapped to a bare root; a Windows UNC or disk prefix leaks through as the last component on non-Windows; source paths produced by out-of-tree builds with trailing separators.
Related errors
- failed to create {dst:?}: {e}
- failed to copy {src:?}->{dst:?}: {e}
- cannot allocate registers
- Size::bits: {bytes} bytes in bits doesn't fit in u64
- Size::add: {} + {} doesn't fit in u64
AI-assisted analysis of rust-lang/rust@22057b88b0 (2026-08-03).
Data as JSON: /data/errors/7c1610d892b5497e.json.
Report an issue: GitHub.