nautechsystems/nautilus_trader · error · anyhow::Error

A different {label} extractor is already registered for '{ty

Error message

A different {label} extractor is already registered for '{type_name}'

What it means

The Python factory's extractor registry allows each type name to have exactly one extractor for a given label. Registering a second, different extractor function for the same type_name bails; re-registering the identical function pointer is idempotent and returns Ok. This prevents silent overwriting of extractor behavior.

Source

Thrown at crates/common/src/python/factory.rs:70

    /// Registering the same extractor again succeeds without change, so a Python module
    /// initializer can run more than once per process.
    ///
    /// # Errors
    ///
    /// Returns an error if a different extractor is already registered for the type name.
    pub fn register(
        &self,
        type_name: String,
        extractor: FactoryExtractor<T>,
    ) -> anyhow::Result<()> {
        let mut extractors = self.extractors_by_type.lock();

        if let Some(registered) = extractors.get(&type_name) {
            if std::ptr::fn_addr_eq(*registered, extractor) {
                return Ok(());
            }

            anyhow::bail!(
                "A different {label} extractor is already registered for '{type_name}'",
                label = self.label
            );
        }

        extractors.insert(type_name, extractor);
        Ok(())
    }

    /// Extracts a Python object into a boxed factory.
    ///
    /// # Errors
    ///
    /// Returns an error if no extractor is registered for the Python type or extraction fails.
    pub fn extract(&self, py: Python<'_>, factory: Py<PyAny>) -> PyResult<Box<T>> {
        let type_name = factory
            .getattr(py, "__class__")?
            .getattr(py, "__name__")?

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Guard registration: only call register if the type name is not already registered, or reuse the same function object.
  2. Unregister/replace explicitly via the appropriate API before registering a different extractor, if supported.
  3. Rename one of the conflicting types or move registration to a single shared module.

Example fix

// before
factory.register("MyData", extractor_v1);
factory.register("MyData", extractor_v2); // bails
// after
if !factory.has_extractor("MyData") {
    factory.register("MyData", extractor_v2);
}
Defensive patterns

Strategy: try-catch

Validate before calling

# skip re-registration when the extractor is unchanged
existing = registry.get(type_name)
if existing is not None and existing is not extractor:
    raise RuntimeError(f"extractor conflict for {type_name}")

Try / catch

try:
    factory.register(type_name, extractor)
except Exception as e:
    if "already registered" in str(e):
        log.debug("extractor for %s already registered", type_name)

Prevention

When it happens

Trigger: Calling register twice with two different extractor functions for the same type_name under the same factory label — typically during module init or repeated imports with differing implementations.

Common situations: Loading two plugin modules that both register extractors for the same custom type; hot-reloading code where a modified extractor function (new pointer) is registered again; test suites registering extractors globally without cleanup between tests.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/307371ee340bebdd. Report an issue: GitHub.