pola-rs/polars · error · PolarsError::InvalidOperation
`unique` operation not supported for dtype `{}`
Error message
`unique` operation not supported for dtype `{}` What it means
agg_n_unique is the group_by aggregation behind n_unique()/approx_n_unique style operations. Before counting distinct values it takes the physical representation; if that representation contains objects (DataType::Object, arbitrary Rust T: PolarsObject values), it panics because hashing/arbitrary object equality is not supported on this path.
Source
Thrown at crates/polars-core/src/frame/group_by/aggregations/dispatch.rs:289
return None;
}
let v = mask.sliced_unchecked(first as usize, len as usize);
let tz = v.trailing_zeros() as IdxSize;
if tz == len { None } else { Some(len - tz - 1) }
})
.collect_ca(PlSmallStr::EMPTY)
},
};
out.into_series()
}
#[doc(hidden)]
pub unsafe fn agg_n_unique(&self, groups: &GroupsType) -> Series {
let values = self.to_physical_repr();
let dtype = values.dtype();
let values = if dtype.contains_objects() {
panic!("{}", polars_err!(opq = unique, dtype));
} else if let Some(ca) = values.try_str() {
ca.as_binary().into_column()
} else if dtype.is_nested() {
encode_rows_unordered(&[values.into_owned().into_column()])
.unwrap()
.into_column()
} else {
values.into_owned().into_column()
};
// Keep the Column for the sort-fallback path. Big groups go through
// `Series::n_unique`, bypassing the amortized hashset.
let col = values.clone();
let values = values.rechunk_to_arrow(CompatLevel::newest());
let values = values.as_ref();
let state = amortized_unique_from_dtype(values.dtype());
struct CloneWrapper(Box<dyn AmortizedUnique>);View on GitHub (pinned to 68506541d2)
Solutions
- Cast or serialize the object column to Utf8/Binary before the aggregation: pl.col("obj").cast(pl.String).n_unique()
- Replace the object column with a supported dtype (struct, list, enum/categorical) at ingestion time
- Drop the object column from the n_unique aggregation and compute uniqueness on a typed key instead
Example fix
# before
df.group_by("k").agg(pl.col("obj").n_unique())
# after
df.group_by("k").agg(pl.col("obj").cast(pl.String).n_unique()) Defensive patterns
Strategy: validation
Validate before calling
fn is_object_col(s: &Series) -> bool {
s.dtype().contains_objects()
} Prevention
- Check dtype().contains_objects() before n_unique aggregations
- Avoid creating Object columns; map external objects to String/Binary/Struct at ingestion
- In Python, avoid object-dtype Series (mixed Python types); inspect pl.Series.dtype before grouping
When it happens
Trigger: A group_by aggregation that needs unique counts over a column whose dtype is Object, e.g. df.group_by(["k"]).agg(pl.col("obj").n_unique()) in Python where "obj" holds Python objects, or the Rust equivalent with a PolarsObject column.
Common situations: polars-python users who created object columns (e.g. via pl.Series with mixed Python objects, or UDFs returning objects); Rust users with custom PolarsObject types; Arrow extension-type data imported as Object.
Related errors
- not implemented
- {:?} -> {:?} not supported
- non-Categorical/Enum dtype in CategoricalChunkedbuilder
- unexpected dtype when deserializing ndjson
- groups are slices not index
AI-assisted analysis of pola-rs/polars@68506541d2 (2026-08-19).
Data as JSON: /api/errors/4b636fe64717e24d.
Report an issue: GitHub.