pola-rs/polars · error

NAMED EXPR REGISTRY NOT SET

Error message

NAMED EXPR REGISTRY NOT SET

What it means

Named anonymous aggregations serialize only a name (plus payload); materialization looks up the implementation in a global registry (NAMED_SERDE_REGISTRY_EXPR) that the embedding must install via set_named_serde_registry. When a serialized plan containing such an agg is deserialized in a process where the registry was never set, the Option<Arc<dyn ExprRegistry>> is None and this expect panics.

Source

Thrown at crates/polars-plan/src/dsl/expr/anonymous/agg.rs:37

impl OpaqueStreamingAgg {
    pub fn materialize(&self) -> PolarsResult<SpecialEq<Arc<dyn AnonymousAgg>>> {
        match self {
            Self::Deserialized(t) => Ok(t.clone()),
            Self::Named {
                name,
                payload,
                value,
            } => feature_gated!("serde", {
                use super::named_serde::NAMED_SERDE_REGISTRY_EXPR;
                match value {
                    Some(v) => Ok(v.clone()),
                    None => Ok(SpecialEq::new(
                        NAMED_SERDE_REGISTRY_EXPR
                            .read()
                            .unwrap()
                            .as_ref()
                            .expect("NAMED EXPR REGISTRY NOT SET")
                            .get_agg(name, payload.as_ref().unwrap())?
                            .expect("NAMED AGG NOT FOUND"),
                    )),
                }
            }),
            Self::Bytes(_b) => {
                feature_gated!("serde", {
                    use crate::dsl::anonymous::serde_expr;
                    serde_expr::deserialize_anon_agg(_b.as_ref()).map(SpecialEq::new)
                })
            },
        }
    }
}

impl Hash for OpaqueStreamingAgg {
    fn hash<H: Hasher>(&self, state: &mut H) {
        core::mem::discriminant(self).hash(state);

View on GitHub (pinned to df599052da)

Solutions

  1. Call set_named_serde_registry(Arc::new(MyRegistry)) at process startup, before any deserialization
  2. Ensure every node that will deserialize plans registers an ExprRegistry covering the same names
  3. If the value variant is cached (value: Some(v)), deserialize is registry-free — prefer sending cached values when possible
  4. Keep producer and consumer on the same polars version so registry names match

Example fix

// before: deserialize in a fresh process -> panic 'NAMED EXPR REGISTRY NOT SET'
let plan = serde_json::from_str::<LazyFrame>(serialized)?;

// after: install the registry at startup
use polars_plan::dsl::named_serde::{set_named_serde_registry, ExprRegistry};
set_named_serde_registry(std::sync::Arc::new(MyAggRegistry) as _);
let plan = serde_json::from_str::<LazyFrame>(serialized)?;
Defensive patterns

Strategy: validation

Validate before calling

// run at process startup, before any deserialization
use polars_plan::dsl::named_serde::{set_named_serde_registry, ExprRegistry};
set_named_serde_registry(std::sync::Arc::new(MyRegistry) as _);

Try / catch

let lf = std::panic::catch_unwind(|| bincode::deserialize::<LazyFrame>(bytes))
    .map_err(|_| anyhow::anyhow!("plan references unregistered named aggregations"))?;

Prevention

When it happens

Trigger: Deserializing (serde) a LazyFrame/expression that contains a named anonymous aggregation in a process that did not call polars_plan::dsl::named_serde::set_named_serde_registry first — e.g. sending a serialized plan to a worker node or a fresh subprocess.

Common situations: Distributed/multi-process setups where plans are shipped between processes; custom embeddings that deserialize expressions without bootstrapping the registry; deserializing in a minimal test harness.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/1d2203a3ab34b61c. Report an issue: GitHub.