pola-rs/polars · error · ValueError
Unable to determine metadata from query result; {self.result
Error message
Unable to determine metadata from query result; {self.result!r} What it means
On the row-wise path, when the result object reports driver_name 'sqlalchemy', polars needs column metadata: it looks for result.cursor.description or result._metadata. A SQLAlchemy result that has fetchall but neither attribute cannot be introspected, so polars raises ValueError with the repr of the result object so you can see what was actually passed. This is a shape mismatch - you handed read_database something that is result-like but not a standard execute() result.
Source
Thrown at py-polars/src/polars/io/database/_executor.py:301
msg = (
"Cannot set `iter_batches` without also setting a non-zero `batch_size`"
)
raise ValueError(msg)
if is_async := isinstance(original_result := self.result, Coroutine):
self.result = _run_async(self.result)
try:
if hasattr(self.result, "fetchall"):
if is_alchemy := (self.driver_name == "sqlalchemy"):
if hasattr(self.result, "cursor"):
cursor_desc = [
(d[0], d[1:]) for d in self.result.cursor.description
]
elif hasattr(self.result, "_metadata"):
cursor_desc = [(k, None) for k in self.result._metadata.keys]
else:
msg = f"Unable to determine metadata from query result; {self.result!r}"
raise ValueError(msg)
elif hasattr(self.result, "description"):
cursor_desc = [(d[0], d[1:]) for d in self.result.description]
else:
cursor_desc = []
schema_overrides = self._inject_type_overrides(
description=cursor_desc,
schema_overrides=(schema_overrides or {}),
)
result_columns = [nm for nm, _ in cursor_desc]
frames = (
DataFrame(
data=rows,
schema=result_columns or None,
schema_overrides=schema_overrides,
infer_schema_length=infer_schema_length,
orient="row",View on GitHub (pinned to df599052da)
Solutions
- Pass the executed result explicitly: result = conn.execute(text(query)); pl.read_database(result, connection=...)
- Upgrade SQLAlchemy to 2.x so results expose the expected metadata attributes
- For a connection string, use pl.read_database_uri(query, uri) instead of read_database with a connection object
Example fix
# before
df = pl.read_database('SELECT * FROM t', connection=engine)
# after
with engine.connect() as conn:
df = pl.read_database('SELECT * FROM t', connection=conn)
# or hand polars the URI directly
df = pl.read_database_uri('SELECT * FROM t', 'postgresql://user:pw@host/db') Defensive patterns
Strategy: type-guard
Validate before calling
def sqlalchemy_result_ok(result) -> bool:
return hasattr(result, 'cursor') or hasattr(result, '_metadata')
result = conn.execute(sqlalchemy_text(query))
assert sqlalchemy_result_ok(result), 'pass an executed SQLAlchemy result'
df = pl.read_database(result, connection=conn) Type guard
def has_sqlalchemy_metadata(obj: object) -> bool:
"""True when polars can introspect this SQLAlchemy result's columns."""
return hasattr(obj, 'fetchall') and (
hasattr(obj, 'cursor') or hasattr(obj, '_metadata')
) Try / catch
try:
df = pl.read_database(result, connection=conn)
except ValueError as err:
if 'Unable to determine metadata' in str(err):
raise TypeError(
'pass the result of conn.execute(), not the engine/connection'
) from err
raise Prevention
- Pass conn.execute(text(sql)) results, never the Engine or a bare Connection, to read_database
- Prefer pl.read_database_uri for plain connection strings - it bypasses this path entirely
- Keep SQLAlchemy >= 2.x so result objects expose the expected metadata attributes
When it happens
Trigger: pl.read_database(query, connection=sqlalchemy_engine) (an Engine, not a result); passing a Connection where polars expected the result of conn.execute(...); exotic/legacy SQLAlchemy result types or third-party wrappers lacking cursor and _metadata; a partially-consumed or closed result whose attributes were released.
Common situations: Passing engine.connect() or the engine itself instead of the executed statement's result; older code written against read_database_uri semantics; mocking SQLAlchemy objects in tests without the expected attributes.
Related errors
- Can't patch loop of type %s
- Cannot set `iter_batches` for {self.driver_name} without als
- Cannot set `iter_batches` without also setting a non-zero `b
- index positions should be smaller than 2^32
- index positions should be greater than or equal to -2^32
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/a1ae5c389cf88be1.
Report an issue: GitHub.