actix/actix-web · error · io::Error
Provided path has no filename
Error message
Provided path has no filename
What it means
An io::Error of kind InvalidInput with message "Provided path has no filename" is returned by get_content_type_and_disposition (named.rs:100) when Path::file_name() returns None. This happens for paths that conceptually have no final component - a trailing "..", the root "/" (or "/"), or an empty path. NamedFile::from_file (named.rs:190) propagates it because it cannot derive a Content-Type or Content-Disposition filename.
Solutions
- Validate that the path has a file name before opening: ensure path.file_name().is_some().
- Reject or normalize paths containing trailing ".." or that equal the root.
- Sanitize user-supplied path segments and never let them reach the filesystem unsanitized.
Example fix
// before
NamedFile::open_async("/").await // Err: no filename
// after
let p = std::path::Path::new("/srv/site/index.html");
assert!(p.file_name().is_some());
NamedFile::open_async(p).await Defensive patterns
Strategy: validation
Validate before calling
// Reject paths without a filename before opening
fn open_named(p: impl AsRef<Path>) -> Result<NamedFile, io::Error> {
let p = p.as_ref();
if p.file_name().is_none() {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "no filename"));
}
NamedFile::open(p)
} Type guard
fn has_filename(p: &Path) -> bool {
p.file_name().map(|n| !n.is_empty()).unwrap_or(false)
} Try / catch
match NamedFile::open_async(&path).await {
Ok(f) => Ok(f.into_response(&req)),
Err(e) if e.kind() == io::ErrorKind::InvalidInput =>
HttpResponse::BadRequest().finish(),
Err(_) => HttpResponse::InternalServerError().finish(),
} Prevention
- Sanitize and normalize user-supplied path segments.
- Reject paths equal to root or ending in '..'.
- Use actix-files Files service which sanitizes routing internally.
When it happens
Trigger: Calling NamedFile::open/open_async or from_file with a path like "/", "", "..", or "/dir/.." - anything where path.file_name() is None. Also hit indirectly via Files directory service if a computed path resolves to such a form.
Common situations: Dynamic path construction joining user input that collapses to root; serving a path that is itself a directory specifier; misconfigured static-file root; symbolic-link resolution landing on "/".
Related errors
- No such file
- Duplicate field found
- invalid Range header: invalid syntax
- invalid Range header: range starts after end of content
- Required field is missing
AI-assisted analysis of actix/actix-web@7ae209e4a4 (2026-08-09).
Data as JSON: /api/errors/92c1e49fb15e09db.
Report an issue: GitHub.
Appendix: source
Thrown at actix-files/src/named.rs:96
pub(crate) flags: Flags,
pub(crate) status_code: StatusCode,
pub(crate) content_type: Mime,
pub(crate) content_disposition: ContentDisposition,
pub(crate) encoding: Option<ContentEncoding>,
pub(crate) read_mode_threshold: u64,
}
pub(crate) use std::fs::File;
use super::chunked;
pub(crate) fn get_content_type_and_disposition(
path: &Path,
) -> Result<(mime::Mime, ContentDisposition), io::Error> {
let filename = match path.file_name() {
Some(name) => name.to_string_lossy(),
None => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Provided path has no filename",
));
}
};
let ct = mime_guess::from_path(path).first_or_octet_stream();
let disposition = match ct.type_() {
mime::IMAGE | mime::TEXT | mime::AUDIO | mime::VIDEO => DispositionType::Inline,
mime::APPLICATION => match ct.subtype() {
mime::JAVASCRIPT | mime::JSON => DispositionType::Inline,
name if name == "wasm" || name == "xhtml" => DispositionType::Inline,
_ => DispositionType::Attachment,
},
_ => DispositionType::Attachment,
};
View on GitHub (pinned to 7ae209e4a4)