pola-rs/polars · error · PolarsError::IO
{}: {path}
Error message
{}: {path} What it means
This error wraps an I/O error (e.g. file-not-found, permission denied) with the full path involved, preserving the original io::ErrorKind. Polars attaches the path so the message stays readable (long paths are truncated in the message, full path available via PathIoError). It is a context-adding wrapper, not a failure class of its own — the underlying kind (NotFound, PermissionDenied, etc.) determines what went wrong.
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 68506541d2)
Solutions
- Verify the path exists and is spelled correctly: run `ls <path>` (or the equivalent for your storage backend) before calling the Polars API.
- Use absolute paths to avoid working-directory surprises, and confirm the path prefix matches the IO plugin/cloud scheme you intend (s3://, gs://, file://).
- Check permissions on the file or parent directory, and on any directories mkdir_recursive will create.
- Inspect the wrapped PathIoError / err.kind() in the message to distinguish NotFound vs PermissionDenied vs other OS errors and fix accordingly.
Example fix
// before
lf = pl.scan_csv("mydat.csv") # FileNotFoundError: path attached
// after
from pathlib import Path
p = Path("data/mydat.csv")
assert p.exists(), f"missing input: {p}"
lf = pl.scan_csv(str(p)) Defensive patterns
Strategy: try-catch
Validate before calling
from pathlib import Path
def ensure_readable(p):
path = Path(p)
if not path.exists():
raise FileNotFoundError(p)
if not path.is_file():
raise IsADirectoryError(p)
if not os.access(path, os.R_OK):
raise PermissionError(p)
return str(path.resolve()) Try / catch
try:
lf = pl.scan_csv(path)
except (FileNotFoundError, PermissionError, OSError) as e:
kind = getattr(getattr(e, '__cause__', e), 'errno', None)
log.error(f"IO failed for path: {e}")
raise Prevention
- Check path existence and permissions before the call
- Use absolute, normalized paths
- Never assume a file checked earlier still exists (TOCTOU) — handle the error
- Log the full wrapped PathIoError to distinguish NotFound vs PermissionDenied
When it happens
Trigger: Any Polars operation that resolves a file path and fails at the OS level: reading/scanning a file (expand_path_cloud, open_blocking, open_file), memory-mapping a file (try_new_mmap_from_path), or creating directories recursively (mkdir_recursive, tokio_mkdir_recursive) when the path does not exist, is misspelled, or lacks permissions.
Common situations: Typos in file paths, reading files that were moved or deleted between existence check and open, relative paths resolved from an unexpected working directory, cloud-storage paths passed without the right prefix/credentials, insufficient permissions on the target path or parent directory when creating directories.
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
- could not read
- activate 'dtype-array'
- validity must be equal to the array's length
- Wrong DataType
- StructArray must be initialized with DataType::Struct
AI-assisted analysis of pola-rs/polars@68506541d2 (2026-09-10).
Data as JSON: /api/errors/a0449cd7f08b0ccc.
Report an issue: GitHub.