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
- Check the path in the message: verify the file/directory exists (Path::exists) and the path is spelled correctly
- Verify read/write permissions on the path and its parent directories
- For cloud paths, verify the URL scheme, bucket, and credentials configuration
- 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
- Check file existence and permissions before launching scans
- Use absolute paths or a known working directory
- For cloud paths, pre-validate scheme/bucket and credentials
- Extract the full path from PathIoError when display is truncated
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)