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

  1. Pass the dtype of the categorical being consumed: source.dtype().clone() instead of a reconstructed DataType
  2. Validate before constructing: matches!(dtype, DataType::Categorical(_, _) | DataType::Enum(_, _))
  3. 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

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


AI-assisted analysis of pola-rs/polars@9b5d73fd00 (2026-08-19). Data as JSON: /api/errors/8c9a0518d5428018. Report an issue: GitHub.