pola-rs/polars · error

NAMED EXPR REGISTRY NOT SET

Error message

NAMED EXPR REGISTRY NOT SET

What it means

Same named-serde mechanism as error 626 but for anonymous column UDFs: deserializing a named function variant requires the global NAMED_SERDE_REGISTRY_EXPR to have been installed via set_named_serde_registry. In a process that never registered it, the RwLock holds None and this expect panics during expression deserialization.

Source

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

impl OpaqueColumnUdf {
    pub fn materialize(self) -> PolarsResult<SpecialEq<Arc<dyn AnonymousColumnsUdf>>> {
        match self {
            Self::Deserialized(t) => Ok(t),
            Self::Named {
                name,
                payload,
                value,
            } => feature_gated!("serde", {
                use super::named_serde::NAMED_SERDE_REGISTRY_EXPR;
                match value {
                    Some(v) => Ok(v),
                    None => Ok(SpecialEq(
                        NAMED_SERDE_REGISTRY_EXPR
                            .read()
                            .unwrap()
                            .as_ref()
                            .expect("NAMED EXPR REGISTRY NOT SET")
                            .get_function(&name, payload.unwrap().as_ref())
                            .expect("NAMED FUNCTION NOT FOUND"),
                    )),
                }
            }),
            Self::Bytes(_b) => {
                feature_gated!("serde";"python", {
                    serde_expr::deserialize_column_udf(_b.as_ref()).map(SpecialEq::new)
                })
            },
        }
    }
}

View on GitHub (pinned to df599052da)

Solutions

  1. Call set_named_serde_registry(...) once per process before deserializing any expression
  2. Use the registry-supporting entrypoint (e.g. the polars build that registers its UDFs on startup) rather than a bare polars_plan dependency
  3. Serialize the cached value variant when the consumer cannot provide the registry
  4. Pin identical polars/extension versions on producer and consumer

Example fix

// before: worker deserializes a plan with a named UDF -> panic
let lf: LazyFrame = bincode::deserialize(bytes)?;

// after: register in every worker before deserialize
set_named_serde_registry(Arc::new(WorkerUdfRegistry) as _);
let lf: LazyFrame = bincode::deserialize(bytes)?;
Defensive patterns

Strategy: validation

Validate before calling

// every worker/subprocess calls this before deserializing expressions
set_named_serde_registry(Arc::new(WorkerUdfRegistry) as _);

Try / catch

let expr = std::panic::catch_unwind(|| serde_json::from_str::<Expr>(s))
    .map_err(|_| anyhow!("named UDF registry not set in this process"))?;

Prevention

When it happens

Trigger: Deserializing a serialized expression/plan containing a named anonymous function (e.g. a Python UDF serialized by name) in a fresh interpreter/process/subprocess that did not set the registry at startup.

Common situations: Multiprocessing or cluster workers receiving pickled/serialized lazy frames; long-lived daemons that deserialize plans created elsewhere; minimal embeddings and test binaries that skip the registry bootstrap.

Related errors


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