pola-rs/polars · error
non-Categorical/Enum dtype in CategoricalChunkedbuilder
Error message
non-Categorical/Enum dtype in CategoricalChunkedbuilder
What it means
CategoricalChunkedBuilder::new(name, dtype) destructures its dtype argument as DataType::Categorical(_, mapping) or DataType::Enum(_, mapping); any other dtype panics immediately with 'non-Categorical/Enum dtype in CategoricalChunkedbuilder'. This is an internal precondition: the builder must be constructed with the exact categorical dtype it will emit, including the categories mapping.
Source
Thrown at crates/polars-core/src/chunked_array/builder/categorical.rs:17
use arrow::bitmap::BitmapBuilder;
use crate::prelude::*;
pub struct CategoricalChunkedBuilder<T: PolarsCategoricalType> {
name: PlSmallStr,
dtype: DataType,
mapping: Arc<CategoricalMapping>,
is_enum: bool,
cats: Vec<T::Native>,
validity: BitmapBuilder,
}
impl<T: PolarsCategoricalType> CategoricalChunkedBuilder<T> {
pub fn new(name: PlSmallStr, dtype: DataType) -> Self {
let (DataType::Categorical(_, mapping) | DataType::Enum(_, mapping)) = &dtype else {
panic!("non-Categorical/Enum dtype in CategoricalChunkedbuilder")
};
Self {
name,
mapping: mapping.clone(),
is_enum: matches!(dtype, DataType::Enum(_, _)),
dtype,
cats: Vec::new(),
validity: BitmapBuilder::new(),
}
}
pub fn dtype(&self) -> &DataType {
&self.dtype
}
pub fn reserve(&mut self, len: usize) {
self.cats.reserve(len);
self.validity.reserve(len);View on GitHub (pinned to 9b5d73fd00)
Solutions
- Pass the dtype of the categorical being consumed: source.dtype().clone() instead of a reconstructed DataType
- Validate before constructing: matches!(dtype, DataType::Categorical(_, _) | DataType::Enum(_, _))
- Prefer higher-level APIs (CategoricalChunked::from_iter, categorical union helpers) that construct the builder with the correct dtype for you
Example fix
// before let b = CategoricalChunkedBuilder::<CategoricalIdxType>::new(name, DataType::String); // panics // after let dtype = DataType::Categorical(None, mapping.clone()); // or source.dtype().clone() let b = CategoricalChunkedBuilder::<CategoricalIdxType>::new(name, dtype);
Defensive patterns
Strategy: type-guard
Validate before calling
// Rust: validate before constructing the builder
assert!(
matches!(dtype, DataType::Categorical(_, _) | DataType::Enum(_, _)),
"CategoricalChunkedBuilder requires a Categorical/Enum dtype, got {dtype}"
); Type guard
fn is_categorical_dtype(dtype: &DataType) -> bool {
matches!(dtype, DataType::Categorical(_, _) | DataType::Enum(_, _))
} Prevention
- Clone the source column's dtype instead of reconstructing DataType by hand
- Keep builder construction behind wrapper functions that own the dtype invariant
- After polars upgrades, grep for DataType::Categorical constructors to confirm the mapping argument is still provided
When it happens
Trigger: Constructing CategoricalChunkedBuilder in Rust with a non-categorical dtype (DataType::String, UInt32, or a Categorical variant from an older polars version without the mapping field); passing a dtype derived from schema inference or a plain Field instead of cloning the source categorical column's dtype.
Common situations: Refactors that rebuild the dtype from a schema rather than cloning the source column's dtype; upgrades across polars versions where Enum and per-column mappings were introduced, leaving stale dtype constructors; copy-pasted builder code where the dtype argument drifted from the data.
Related errors
- not implemented
- {:?} -> {:?} not supported
- unexpected dtype when deserializing ndjson
- `unique` operation not supported for dtype `{}`
- Invalid `POLARS_PQ_PREFILTERED_MASK` value '{v}'.
AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19).
Data as JSON: /api/errors/8c9a0518d5428018.
Report an issue: GitHub.