{"record":{"id":"4f753ed9b2ddaede","repo":"pola-rs/polars","slug":"source-must-implement-the-arrow-pycapsule-interfac","errorCode":null,"errorMessage":"source must implement the Arrow PyCapsule Interface (__arrow_c_stream__)","messagePattern":"source must implement the Arrow PyCapsule Interface \\(__arrow_c_stream__\\)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/io/arrow_c_stream.py","lineNumber":76,"sourceCode":"    ... ]\n    >>> reader = pa.RecordBatchReader.from_batches(schema, batches)\n    >>> pl.scan_arrow_c_stream(reader).collect()\n    shape: (5, 2)\n    ┌─────┬─────┐\n    │ a   ┆ b   │\n    │ --- ┆ --- │\n    │ i64 ┆ str │\n    ╞═════╪═════╡\n    │ 1   ┆ x   │\n    │ 2   ┆ y   │\n    │ 3   ┆ z   │\n    │ 4   ┆ a   │\n    │ 5   ┆ b   │\n    └─────┴─────┘\n    \"\"\"\n    if not hasattr(source, \"__arrow_c_stream__\"):\n        msg = \"source must implement the Arrow PyCapsule Interface (__arrow_c_stream__)\"\n        raise TypeError(msg)\n\n    import polars._plr as plr\n\n    reader = plr.PyArrowCStreamReader(source)\n\n    def io_source(\n        with_columns: list[str] | None,\n        predicate: Expr | None,\n        n_rows: int | None,\n        batch_size: int | None,  # noqa: ARG001\n    ) -> Iterator[DataFrame]:\n        remaining = n_rows\n        while (batch := reader.next_batch(with_columns)) is not None:\n            df = pl.DataFrame._from_pydf(batch)\n            if predicate is not None:\n                df = df.filter(predicate)\n            if remaining is not None:\n                df = df.head(remaining)","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/io/arrow_c_stream.py#L58-L94","documentation":"pl.scan_arrow_c_stream (py-polars/src/polars/io/arrow_c_stream.py:21) builds a LazyFrame from any object implementing the Arrow PyCapsule Interface, i.e. exposing a __arrow_c_stream__ method (e.g. a pyarrow RecordBatchReader or a nanoarrow stream). The first thing it does is hasattr(source, '__arrow_c_stream__'); anything else - a pyarrow Table, a pandas DataFrame, a path string, or a pre-PyArrow-14 object lacking the method - raises this TypeError immediately.","triggerScenarios":"pl.scan_arrow_c_stream(pa.table({'a': [1]})); passing a pandas DataFrame, numpy array, or file path; passing a pyarrow.RecordBatchReader created by pyarrow < 14, which does not yet expose the capsule method.","commonSituations":"Generic interop code that accepts 'anything arrow-ish'; CI pinned to an older pyarrow than the developer machine; users assuming this unstable API is a drop-in replacement for pl.from_arrow.","solutions":["Pass a capsule-capable stream object: reader = tbl.to_reader() (pyarrow >= 14) and scan that","Upgrade pyarrow to >= 14 so Table/RecordBatchReader expose __arrow_c_stream__","For non-capsule Arrow inputs use the eager pl.from_arrow(source) instead","Branch in generic ingestion code on hasattr(source, '__arrow_c_stream__') before choosing the API"],"exampleFix":"# before\nlf = pl.scan_arrow_c_stream(pa.table({\"a\": [1]}))  # TypeError\n# after\nreader = pa.table({\"a\": [1]}).to_reader()\nlf = pl.scan_arrow_c_stream(reader)","handlingStrategy":"type-guard","validationCode":"def to_c_stream_source(obj):\n    if hasattr(obj, \"__arrow_c_stream__\"):\n        return obj\n    if hasattr(obj, \"to_reader\"):  # pyarrow Table\n        return obj.to_reader()\n    raise TypeError(f\"cannot adapt {type(obj).__name__} to an Arrow C stream\")","typeGuard":"def supports_arrow_c_stream(source: object) -> bool:\n    return hasattr(source, \"__arrow_c_stream__\") and callable(source.__arrow_c_stream__)","tryCatchPattern":"try:\n    lf = pl.scan_arrow_c_stream(source)\nexcept TypeError as e:\n    if \"PyCapsule\" in str(e):\n        lf = pl.from_arrow(source).lazy()  # eager fallback for legacy Arrow inputs\n    else:\n        raise","preventionTips":["Standardize on pyarrow >= 14 when using capsule-based APIs","Gate generic ingestion behind hasattr(source, '__arrow_c_stream__')","Remember scan_arrow_c_stream is @unstable; pin polars versions where it is used"],"tags":["polars","arrow","pycapsule","interop","typeerror"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}