actix/actix-web · error · io::Error

Provided path has no filename

Error message

Provided path has no filename

What it means

io::Error with kind InvalidInput and message 'Provided path has no filename' (named.rs:99-104) is returned by get_content_type_and_disposition when Path::file_name() returns None — the path's final component is a root or '..'. It propagates out of NamedFile::from_file / open / open_async, which call this helper to derive Content-Type and Content-Disposition. This is a programmer error in the path argument, not a runtime/network condition.

Source

Thrown at actix-files/src/named.rs:100

    pub(crate) encoding: Option<ContentEncoding>,
    pub(crate) read_mode_threshold: u64,
}

#[cfg(not(feature = "experimental-io-uring"))]
pub(crate) use std::fs::File;

#[cfg(feature = "experimental-io-uring")]
pub(crate) use tokio_uring::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 937960ca67)

Solutions

  1. Ensure the path passed to NamedFile::open/open_async/from_file has a real filename as its final component.
  2. Validate user-derived paths with Path::file_name() before constructing a NamedFile.
  3. Use Files (directory serving) for directory paths instead of NamedFile.

Example fix

// before: path normalises to no filename
NamedFile::open_async(base_dir.join(user_input)).await  // user_input could be ".."

// after: require a concrete filename
let name = path.file_name().ok_or_else(||
    io::Error::new(io::ErrorKind::InvalidInput, "path must name a file"))?
NamedFile::open_async(base_dir.join(name)).await
Defensive patterns

Strategy: validation

Validate before calling

// Validate that the path ends in a real filename before opening.
use std::path::Path;
fn ensure_has_filename(p: &Path) -> io::Result<()> {
    if p.file_name().is_none() {
        Err(io::Error::new(io::ErrorKind::InvalidInput, "path has no filename"))
    } else { Ok(()) }
}

Try / catch

// Handle the io::Result from open_async.
match NamedFile::open_async(path).await {
    Ok(f) => Ok(f),
    Err(e) if e.kind() == io::ErrorKind::InvalidInput => {
        // path had no filename; reject the request as 400
        Err(actix_web::error::ErrorBadRequest("invalid file path"))
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling NamedFile::open("/"), NamedFile::open("."), NamedFile::from_file(file, "foo/.."), or any path whose last segment normalises away. file_name() returns None for such paths, hitting the error arm.

Common situations: Building a NamedFile path dynamically from user input or joining that yields a trailing separator, or passing a directory path where a file path was expected.

Related errors


AI-assisted analysis of actix/actix-web@937960ca67 (2026-08-06). Data as JSON: /data/errors/92c1e49fb15e09db.json. Report an issue: GitHub.