nautechsystems/nautilus_trader · error

Custom data type "{type_name}" is already registered for Pyt

Error message

Custom data type "{type_name}" is already registered for Python extraction

What it means

register_py_extractor registers a Python object extractor for a custom data type name in the Python-feature registry. The registry rejects duplicate names with this error so an existing extractor is never silently replaced.

Source

Thrown at crates/model/src/data/registry.rs:271

#[cfg(feature = "python")]
fn py_extractors() -> &'static DashMap<String, PyExtractor> {
    static PY_EXTRACTORS: std::sync::OnceLock<DashMap<String, PyExtractor>> =
        std::sync::OnceLock::new();
    PY_EXTRACTORS.get_or_init(DashMap::new)
}

/// Registers a `PyExtractor` for the given custom data type name.
/// Used by `CustomData` constructor to convert Python objects to `Arc<dyn CustomDataTrait>`.
///
/// # Errors
/// Returns an error if the type is already registered.
#[cfg(feature = "python")]
pub fn register_py_extractor(type_name: &str, extractor: PyExtractor) -> Result<(), anyhow::Error> {
    let reg = py_extractors();
    match reg.entry(type_name.to_string()) {
        Entry::Occupied(_) => {
            anyhow::bail!(
                "Custom data type \"{type_name}\" is already registered for Python extraction"
            );
        }
        Entry::Vacant(v) => {
            v.insert(extractor);
            Ok(())
        }
    }
}

/// Registers a `PyExtractor` for the given custom data type name if not already registered.
/// If the type is already registered, returns `Ok(())` without overwriting (idempotent).
/// Use this where repeated registration can occur (e.g. module init).
///
/// # Errors
/// Does not return an error (idempotent insert into `DashMap`).
#[cfg(feature = "python")]
pub fn ensure_py_extractor_registered(

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Guard registration with a once-only mechanism or an initialized flag in the adapter.
  2. Catch this error and treat it as a no-op when re-registering the identical extractor.
  3. Use a unique type name if the collision is between distinct types.

Example fix

// before
def register():
    register_py_extractor("MyData", extractor)  # fails on reload
// after
_registered = False
def register():
    global _registered
    if not _registered:
        register_py_extractor("MyData", extractor)
        _registered = True
Defensive patterns

Strategy: try-catch

Validate before calling

# Python
if type_name in getattr(_module, "_py_extractors_registered", set()):
    return
_module._py_extractors_registered.add(type_name)

Try / catch

# Python
try:
    register_py_extractor(type_name, extractor)
except Exception as e:
    if "already registered for Python extraction" in str(e):
        pass  # idempotent re-init
    else:
        raise

Prevention

When it happens

Trigger: Calling register_py_extractor twice for the same type_name — re-imported Python modules, repeated adapter instantiation, or a type registered by both a library crate and user code.

Common situations: Jupyter notebook re-execution re-running registration cells; pytest fixtures instantiating the adapter multiple times; dynamic module reloads.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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