pola-rs/polars · error · TypeError
`path` argument has invalid type {qualified_type_name(path)!
Error message
`path` argument has invalid type {qualified_type_name(path)!r}, and cannot be turned into a sink target What it means
The private helper `_to_sink_target` (py-polars/src/polars/lazyframe/engine.py:64) normalizes the `path` argument of every local sink operation (`sink_parquet`, `sink_ipc`, `sink_csv`, `sink_ndjson`). It only accepts `str`, `pathlib.Path`, an `io.IOBase` file object, a `PartitionBy` target, or any object with a callable `.write` attribute (custom writer). Any other type is rejected up front with this TypeError so the query never starts.
Source
Thrown at py-polars/src/polars/lazyframe/engine.py:64
def _to_sink_target(
path: str | Path | IO[bytes] | IO[str] | PartitionBy,
) -> str | Path | IO[bytes] | IO[str] | PartitionBy:
from polars.io.partition import PartitionBy
if isinstance(path, (str, Path)):
return normalize_filepath(path)
elif isinstance(path, io.IOBase):
return path
elif isinstance(path, PartitionBy):
return path
elif callable(getattr(path, "write", None)):
# This allows for custom writers
return path
else:
msg = f"`path` argument has invalid type {qualified_type_name(path)!r}, and cannot be turned into a sink target"
raise TypeError(msg)
def _with_monitoring(optimizations: QueryOptFlags) -> QueryOptFlags:
"""Register the query observer, and flag `optimizations` accordingly."""
monitor = os.environ.get("POLARS_QUERY_MONITORING") == "1"
if monitor:
import polars._plr as plr
plr.set_query_monitoring(True)
optimizations = optimizations.__copy__()
optimizations._pyoptflags.query_monitoring = monitor
return optimizations
def _apply_retries_deprecation(
retries: int | None, storage_options: StorageOptionsDict | None
) -> StorageOptionsDict | None:View on GitHub (pinned to df599052da)
Solutions
- Pass a `str` or `pathlib.Path` file path: `lf.sink_parquet('out.parquet')`
- Pass a real opened binary file object, e.g. `open('out.parquet','wb')` or `io.BytesIO()`
- For partitioned multi-file output pass `pl.PartitionBy(...)` as the target
- For a custom destination, pass an object implementing a callable `.write` method, or use `lf.sink_batches(fn)` for per-batch callbacks
Example fix
# before
lf.sink_parquet(b'not-a-path')
# after
lf.sink_parquet('out.parquet') # str/Path, open('out.parquet','wb'), or pl.PartitionBy(...) Defensive patterns
Strategy: type-guard
Validate before calling
import io
from pathlib import Path
from polars.io.partition import PartitionBy
def is_sink_target(path: object) -> bool:
return (
isinstance(path, (str, Path, io.IOBase, PartitionBy))
or callable(getattr(path, 'write', None))
)
# before sinking:
assert is_sink_target(path), f'bad sink target: {type(path).__name__}' Type guard
from typing import TypeGuard
import io
from pathlib import Path
from polars.io.partition import PartitionBy
def is_sink_target(path: object) -> TypeGuard[str | Path | io.IOBase | PartitionBy]:
return (
isinstance(path, (str, Path, io.IOBase, PartitionBy))
or callable(getattr(path, 'write', None))
) Try / catch
try:
lf.sink_parquet(path)
except TypeError as e:
if 'cannot be turned into a sink target' in str(e):
raise ValueError(f'unsupported sink path {path!r}') from e
raise Prevention
- Always build sink paths as str or pathlib.Path from the start
- Wrap foreign file-like objects so they subclass io.IOBase or expose .write
- Keep a single is_sink_target guard next to any API boundary that accepts user-supplied destinations
- For multiple outputs use PartitionBy instead of passing a list of paths
When it happens
Trigger: Calling `lf.sink_parquet(path)` / `lf.sink_ipc(path)` / `lf.sink_csv(path)` / `lf.sink_ndjson(path)` (or the same on any local engine: in-memory, streaming, gpu, auto) with e.g. an int, `bytes`, a `list` of paths, `None`, `os.DirEntry`, or a test mock that has no `.write` method.
Common situations: Passing file content (`bytes`) instead of a file path; passing a list of paths expecting multi-file output (use `pl.PartitionBy` instead); passing a Path-like object from a third-party VFS library that is neither `io.IOBase` nor exposes `.write`; stubbing sinks in tests with objects that lack a callable `write`.
Related errors
- the remote engine can only sink to a URI or a `PartitionBy`,
- `compat_level` has invalid type: {qualified_type_name(compat
- cannot select columns using key of type {qualified_type_name
- cannot select rows using key of type {qualified_type_name(ke
- cannot treat Series of type {s.dtype} as indices
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/32059323ea044a46.
Report an issue: GitHub.