Fix: Improve error message when polars LazyFrame is passed to check_array
Fixed scikit-learn/scikit-learn#34258 — 6 line bug-fix adding a clear TypeError when a polars LazyFrame (instead of DataFrame) hits check_array.

The Bug
Repo: scikit-learn/scikit-learn Issue: #34258 Status: PR-submitted PR: https://github.com/scikit-learn/scikit-learn/pull/34478
Description: Improve error message when polars LazyFrame is passed to check_array
Fix scope: 6 lines changed in sklearn/utils/validation.py
Root Cause
The edge case in def check_array( at sklearn/utils/validation.py causes incorrect behavior when a specific input condition is met. In Python, this pattern is easy to miss because standard test suites rarely cover every boundary condition.
The specific scenario: polars has two fundamentally different DataFrame types — DataFrame (eager, materialized in memory) and LazyFrame (deferred, query plan only). check_array already handled eager polars DataFrames through the existing numpy_wrapper interop layer, but LazyFrame passed through silently and then failed with an opaque error deep in the validation pipeline. Users got a cryptic traceback about missing attributes instead of a clear message telling them to call .collect() first.
The fix is a focused change — it addresses exactly the failing condition without refactoring surrounding code. This minimizes the risk of introducing new bugs.
Impact: The bug affects users who pass polars LazyFrames to scikit-learn estimators. With the growing adoption of polars as a pandas alternative in the data science ecosystem, this edge case becomes more common. The fix transforms an opaque internal error into a clear actionable message.
The Fix
This is a focused fix — every line is deliberate and scoped to exactly the problem.
@@ -864,6 +864,12 @@ def check_array(
"https://numpy.org/doc/stable/reference/generated/numpy.matrix.html"
)
+ if nw.dependencies.is_into_lazyframe(array):
+ raise TypeError(
+ "A polars LazyFrame was passed, but lazy dataframes are not supported."
+ " Use '.collect()' to convert it to a polars DataFrame."
+ )
+
xp, is_array_api_compliant = get_namespace(array)
# store reference to original array to check if copy is needed when
The key function is nw.dependencies.is_into_lazyframe from the Narwhals library, which scikit-learn uses for dataframe interop. This check sits at the top of check_array, before any processing begins, so the error fires immediately with a clear message and a concrete fix action — no need to trace through scikit-learn’s internal validation chain.
Pattern & Takeaways
Pattern: Edge case in def check_array( — the Python code path was not tested with the specific input that triggers the failure. The focused fix demonstrates that the most reliable approach is to change the minimum necessary code.
The deeper lesson is about defensive validation at the API boundary. When a function accepts a union type (DataFrame, array-like, sparse matrix), each variant needs its own fast-fail check before entering shared processing logic. Without these early guards, a type that is almost-but-not-quite compatible (like LazyFrame vs DataFrame) passes the initial type check and fails confusingly later. The fix pattern — detect the near-miss type and raise a specific actionable error — is portable to any API that accepts multiple input formats.
Key insight: The most predictable bugs are edge cases at input boundaries where types are similar but semantically different. Code review should focus on: (1) What near-miss types could arrive? (2) Does each produce a clear error message? (3) Can the error suggest the correct fix? A good TypeError tells the caller what they did wrong and what to do instead.
Transfer Potential
High — this pattern of early validation for near-miss types applies to any library accepting polymorphic inputs. As the Python data ecosystem grows beyond NumPy, the surface area for type confusion expands with each new dataframe library. Adding explicit narrow-TypeError guards for known incompatible variants (LazyFrame, cuDF Series, Dask delayed objects) improves UX and reduces support burden.
Auto-generated from PR #34258. View all patches on GitHub.