pola-rs/polars · error · NotImplementedError

`sink_csv` is not supported by {type(self).__name__}

Error message

`sink_csv` is not supported by {type(self).__name__}

What it means

`Engine.sink_csv` (engine.py:306) is the optional `Engine` hook behind `LazyFrame.sink_csv`. Its default stub raises `NotImplementedError` naming the engine. All built-in engines implement it (locally, or remotely via Polars Cloud with a restricted option set), so in practice only custom `Engine` subclasses missing the override trigger it.

Source

Thrown at py-polars/src/polars/lazyframe/engine.py:306

        date_format: str | None,
        time_format: str | None,
        float_scientific: bool | None,
        float_precision: int | None,
        decimal_comma: bool,
        null_value: str | None,
        quote_style: CsvQuoteStyle | None,
        maintain_order: bool,
        storage_options: StorageOptionsDict | None,
        credential_provider: CredentialProviderFunction | Literal["auto"] | None,
        retries: int | None,
        sync_on_close: SyncOnCloseMethod | None,
        mkdir: bool,
        lazy: bool,
        optimizations: QueryOptFlags,
    ) -> LazyFrame | None:
        """See :meth:`polars.LazyFrame.sink_csv`."""
        msg = f"`sink_csv` is not supported by {type(self).__name__}"
        raise NotImplementedError(msg)

    def sink_ndjson(
        self,
        lf: LazyFrame,
        path: str | Path | IO[bytes] | IO[str] | PartitionBy,
        *,
        compression: Literal["uncompressed", "gzip", "zstd"],
        compression_level: int | None,
        check_extension: bool,
        maintain_order: bool,
        storage_options: StorageOptionsDict | None,
        credential_provider: CredentialProviderFunction | Literal["auto"] | None,
        retries: int | None,
        sync_on_close: SyncOnCloseMethod | None,
        mkdir: bool,
        lazy: bool,
        optimizations: QueryOptFlags,
    ) -> LazyFrame | None:

View on GitHub (pinned to df599052da)

Solutions

  1. Use a built-in engine: `lf.sink_csv(path, engine='streaming')` (default), or `engine=pl.RemoteEngine()` with only the supported options
  2. Implement `sink_csv` on your `Engine` subclass with the stub's full keyword signature
  3. Stopgap: `lf.collect().write_csv(path)`
  4. Detect the missing capability before calling when engines are pluggable

Example fix

# before
lf.sink_csv('out.csv', engine=my_custom_engine)  # NotImplementedError

# after
lf.sink_csv('out.csv', engine='streaming')
# or implement `sink_csv` on MyEngine
Defensive patterns

Strategy: validation

Validate before calling

from polars.lazyframe.engine import Engine

def engine_can_sink_csv(engine: pl.Engine) -> bool:
    return type(engine).sink_csv is not Engine.sink_csv

engine = my_engine if engine_can_sink_csv(my_engine) else pl.StreamingEngine()

Try / catch

try:
    lf.sink_csv(path, engine=my_engine)
except NotImplementedError as e:
    if 'sink_csv' in str(e):
        lf.collect().write_csv(path)
    else:
        raise

Prevention

When it happens

Trigger: `lf.sink_csv(path, engine=my_engine)` where `my_engine` is a custom `pl.Engine` subclass without `sink_csv`. Note that with `RemoteEngine` this method exists but rejects unsupported options with a different ValueError (see error 456).

Common situations: Custom backend development where CSV export is deferred; mocking engines in tests of export pipelines; switching a sink-heavy workload to a plugin engine before all sink formats are implemented.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/a6d072fd6412fcea. Report an issue: GitHub.