pola-rs/polars · error · FileNotFoundError
no workbook found at path {src!r}
Error message
no workbook found at path {src!r} What it means
FileNotFoundError raised while normalizing read_excel sources. When a str/PathLike source does not exist as a literal path, polars expands '~' and treats it as a glob pattern (glob.glob(src, recursive=True)); if the pattern matches nothing, the file was neither a real path, a URL, nor a matching glob, so reading cannot proceed. Note this fire-once message names the expanded pattern.
Source
Thrown at py-polars/src/polars/io/spreadsheet/functions.py:77
read_multiple_workbooks = True
sources: list[Any] = []
if isinstance(source, memoryview):
source = source.tobytes()
if not isinstance(source, Sequence) or isinstance(source, (bytes, str)):
read_multiple_workbooks = False
source = [source] # type: ignore[assignment]
for src in source: # type: ignore[union-attr]
if isinstance(src, (str, os.PathLike)) and not Path(src).exists():
src = os.path.expanduser(str(src)) # noqa: PTH111
if looks_like_url(src):
sources.append(src)
continue
sources.extend(files := glob(src, recursive=True)) # noqa: PTH207
if not files:
msg = f"no workbook found at path {src!r}"
raise FileNotFoundError(msg)
read_multiple_workbooks = True
else:
if isinstance(src, os.PathLike):
src = str(src)
sources.append(src)
return sources, read_multiple_workbooks
def _standardize_duplicates(s: str) -> str:
"""Standardize columns with '_duplicated_n' names."""
return re.sub(r"_duplicated_(\d+)", repl=r"\1", string=s)
def _unpack_read_results(
frames: list[pl.DataFrame] | list[dict[str, pl.DataFrame]],
*,
read_multiple_workbooks: bool,View on GitHub (pinned to df599052da)
Solutions
- If the filename is literal, check it exists first and escape metacharacters: src = glob.escape('data[1].xlsx') or pass a resolved Path after verifying Path(src).exists().
- If it should be a glob, verify the pattern and working directory: print(glob.glob(pattern, recursive=True)) before calling read_excel.
- For URLs, ensure the string is recognized as such (full https://... form) so it is not glob-expanded.
- Guard with if not Path(src).exists() and not glob.glob(src): raise a clearer error with the intended location.
Example fix
# before
pl.read_excel('data[1].xlsx') # file exists, but '[' breaks the glob fallback
# after
import glob, pathlib
src = 'data[1].xlsx'
assert pathlib.Path(src).exists()
pl.read_excel(glob.escape(src) if any(c in src for c in '[]*?') else src) Defensive patterns
Strategy: validation
Validate before calling
import glob, os
from pathlib import Path
def resolve_excel_source(src: str) -> str:
if Path(src).exists():
return src
expanded = os.path.expanduser(src)
if Path(expanded).exists():
return expanded
if glob.glob(expanded, recursive=True):
return expanded
raise FileNotFoundError(f'no workbook at {src!r} (exists={Path(src).exists()})')
pl.read_excel(resolve_excel_source(src)) Try / catch
try:
df = pl.read_excel(src)
except FileNotFoundError as e:
if 'no workbook found at path' in str(e):
raise ValueError(f'check path/glob {src!r}: cwd={os.getcwd()}') from e
raise Prevention
- Prefer pathlib.Path objects and assert Path(src).exists() for literal files.
- Escape glob metacharacters in literal filenames: glob.escape(name).
- Remember polars falls back to recursive globbing for non-existent str paths — verify patterns against cwd.
When it happens
Trigger: pl.read_excel('~/reports/data_2024_*.xlsx') matching nothing; pl.read_excel('data[1].xlsx') where the literal file exists but '[' makes it an invalid/empty glob character class; plain wrong paths.
Common situations: Filenames containing glob metacharacters ([, ], *, ?) — very common with bracketed indices or dates; running on a different OS where the path separator or expansion differs; typos or files not yet downloaded; passing Path objects to files created asynchronously.
Related errors
- no data found in the given workbook(s) and sheet(s)
- no matching sheets found when `sheet_{param}` is {value!r}
- cannot specify both `columns` and `read_options["use_columns
- the values of `has_header` and `read_options["header_row"]`
- cannot specify both `infer_schema_length` and `read_options[
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/614f333b468ed92d.
Report an issue: GitHub.