pola-rs/polars · error · PathIoError

{source}: {path}

Error message

{source}: {path}

What it means

Polars wraps I/O errors with the offending file path via _limit_path_len_io_err, producing a PolarsError whose message is formatted as '{source}: {path}'. The original io::ErrorKind is preserved and the full path is available in the PathIoError payload; the path may be truncated in the display message if very long. This wrapper is applied by path-based operations such as opening files, memory-mapping, mkdir_recursive, and cloud path expansion.

Solutions

  1. Check the path in the message: verify the file/directory exists (Path::exists) and the path is spelled correctly
  2. Verify read/write permissions on the path and its parent directories
  3. For cloud paths, verify the URL scheme, bucket, and credentials configuration
  4. Match on the PolarsError/PathIoError payload to recover the full untruncated path programmatically

Example fix

// before
let df = pl.scan_parquet("data/fiel.parquet").collect()?; // typo
// after
let path = std::path::Path::new("data/file.parquet");
assert!(path.exists(), "missing input: {}", path.display());
let df = pl.scan_parquet(path).collect()?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: verify path accessibility before reading
let path = std::path::Path::new("data/file.parquet");
assert!(path.exists(), "path does not exist: {}", path.display());
assert!(path.is_file(), "path is not a file: {}", path.display());
std::fs::File::open(path).expect("path not readable");

Type guard

fn readable_file(p: &std::path::Path) -> bool { p.is_file() && std::fs::File::open(p).is_ok() }

Try / catch

match lf.collect() {
    Err(PolarsError::IO(err)) if err.kind() == io::ErrorKind::NotFound => { /* log full path, prompt user */ },
    other => other?,
}

Prevention

When it happens

Trigger: Any failing filesystem or cloud-storage operation on a path: scan_csv/scan_parquet/read_ipc with a nonexistent file, permission-denied paths, failed mkdir chains, or invalid cloud URLs expanded via expand_path_cloud.

Common situations: Typos in file paths; files moved/deleted between check and read; missing cloud credentials surfacing as path errors; permission-restricted directories; relative paths resolved from an unexpected working directory.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of pola-rs/polars@fe841f959e (2026-09-18). Data as JSON: /api/errors/ecf7e469a789ecb4. Report an issue: GitHub.

Appendix: source

Thrown at crates/polars-utils/src/io.rs:52

                self.source
            )
        } else {
            write!(f, "{}: {path}", self.source)
        }
    }
}

impl std::error::Error for PathIoError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        Some(&self.source)
    }
}

/// Attaches `path` to `err`, keeping its [`io::ErrorKind`].
///
/// The path is available in full through [`PathIoError`], and truncated in the message.
pub fn _limit_path_len_io_err(path: &Path, err: io::Error) -> PolarsError {
    io::Error::new(
        err.kind(),
        PathIoError {
            path: path.to_path_buf(),
            source: err,
        },
    )
    .into()
}

pub fn open_file(path: &Path) -> PolarsResult<File> {
    File::open(path).map_err(|err| _limit_path_len_io_err(path, err))
}

pub fn open_file_write(path: &Path) -> PolarsResult<File> {
    std::fs::OpenOptions::new()
        .write(true)
        .create(true)
        .truncate(true)

View on GitHub (pinned to fe841f959e)