{"record":{"id":"7a23bfd6bc42b0d0","repo":"nautechsystems/nautilus_trader","slug":"a-different-simulation-module-extractor-is-already","errorCode":null,"errorMessage":"A different simulation module extractor is already registered for '{type_name}'","messagePattern":"A different simulation module extractor is already registered for '(.+?)'","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/backtest/src/python/modules.rs","lineNumber":74,"sourceCode":"///\n/// Registering the same function for the same Python type more than once succeeds without change.\n///\n/// # Errors\n///\n/// Returns an error if a different extractor is already registered for `T`.\npub fn register_simulation_module_extractor<T: PyClass>(\n    py: Python<'_>,\n    extractor: SimulationModuleExtractor,\n) -> anyhow::Result<()> {\n    let type_object = py.get_type::<T>();\n    let type_id = type_object.as_ptr() as usize;\n    let type_name = type_object.name()?;\n    let mut extractors = SIMULATION_MODULE_EXTRACTORS.lock();\n    if let Some(registered) = extractors.get(&type_id) {\n        if std::ptr::fn_addr_eq(*registered, extractor) {\n            return Ok(());\n        }\n        anyhow::bail!(\n            \"A different simulation module extractor is already registered for '{type_name}'\"\n        );\n    }\n    extractors.insert(type_id, extractor);\n    Ok(())\n}\n\n#[pyo3_stub_gen::derive::gen_stub_pyclass(module = \"nautilus_trader.backtest\")]\n#[pyclass(\n    module = \"nautilus_trader.backtest\",\n    name = \"SimulationModule\",\n    subclass\n)]\n#[derive(Debug)]\npub struct PySimulationModule;\n\n#[pyo3_stub_gen::derive::gen_stub_pymethods]\n#[pymethods]","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/python/modules.rs#L56-L92","documentation":"register_simulation_module_extractor registers a Python-side extractor for a simulation module type, keyed by the type's TypeId in a global registry. If a type is registered a second time with a DIFFERENT extractor function, registration fails; re-registering the identical extractor is a no-op that returns Ok. This guards against one module silently overriding another's extractor.","triggerScenarios":"Calling register_simulation_module_extractor twice for the same Python type with two different extractor callables, or two libraries importing the same type and each installing their own extractor.","commonSituations":"Loading multiple plugin packages that both extend the same simulation module type; re-registering during a test harness with a freshly defined but semantically different extractor; hot-reloading a module whose extractor closure changed.","solutions":["Register each type's extractor exactly once at process startup, before simulation runs","If the different extractor is intentional, remove the previous registration or use a distinct Python type","Check for duplicate imports/plugins that both call register_simulation_module_extractor for the same type","If re-registering the same extractor, no error occurs — ensure you pass the identical function object, not a re-created wrapper"],"exampleFix":"// before\nregister_simulation_module_extractor(MyModule, my_extractor_v1)\nregister_simulation_module_extractor(MyModule, my_extractor_v2)  # bails\n// after\nregister_simulation_module_extractor(MyModule, my_extractor_v2)  # register once, final version","handlingStrategy":"validation","validationCode":"def ensure_registered(type_obj, extractor, _seen={}):\n    key = type_obj\n    if key in _seen and _seen[key] is not extractor:\n        raise RuntimeError(f\"extractor already registered differently for {type_obj.__name__}\")\n    _seen[key] = extractor","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Register all extractors in one central module imported once at startup","Keep a module-level registry of (type, extractor) pairs to detect duplicates early","Avoid re-created wrapper closures; use named module-level functions","In tests, use fresh registry state per test to avoid cross-test conflicts"],"tags":["rust","backtest","registration","conflict"],"backgroundTag":"internal-invariant-violation","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}