nautechsystems/nautilus_trader · error

{field} must be in format 'module.path:ClassName'

Error message

{field} must be in format 'module.path:ClassName'

What it means

Thrown by `split_import_path` when an importable path string is missing the required `module.path:ClassName` separator. The library resolves actors and strategies from Python import paths and needs exactly one `:` separating the dotted module from the class name. The error names the offending field (e.g. 'controller_path' or 'strategy_path') so you know which config value is malformed.

Source

Thrown at crates/system/src/python/registration.rs:528

    log::info!("Importing strategy from module: {module_name} class: {class_name}");

    Python::attach(|py| -> anyhow::Result<Py<PyAny>> {
        let strategy_class = import_python_class(py, module_name, class_name)?;
        let config_instance = create_config_instance(py, &config.config_path, &config.config)?;

        let python_strategy = if let Some(config_obj) = config_instance.as_ref() {
            strategy_class.call1((config_obj,))?
        } else {
            strategy_class.call0()?
        };

        Ok(python_strategy.unbind())
    })
}

fn split_import_path<'a>(path: &'a str, field: &str) -> anyhow::Result<(&'a str, &'a str)> {
    let Some((module_name, class_name)) = path.split_once(':') else {
        anyhow::bail!("{field} must be in format 'module.path:ClassName'");
    };

    if module_name.is_empty() || class_name.is_empty() || class_name.contains(':') {
        anyhow::bail!("{field} must be in format 'module.path:ClassName'");
    }

    Ok((module_name, class_name))
}

fn import_python_class<'py>(
    py: Python<'py>,
    module_name: &str,
    class_name: &str,
) -> anyhow::Result<Bound<'py, PyAny>> {
    let module = py
        .import(module_name)
        .map_err(|e| anyhow::anyhow!("Failed to import module {module_name}: {e}"))?;

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Format the path as `module.path:ClassName`, e.g. `my_pkg.strategies:MyStrategy`
  2. Verify the class name after the colon is non-empty and contains no further colons
  3. Check your config file for the field named in the error message and correct its value
  4. Test the import manually with `importlib.import_module('module.path')` and `getattr` to confirm the class exists

Example fix

// before
controller_path = "nautilus_trader.examples.controllers.MyController"
// after
controller_path = "nautilus_trader.examples.controllers:MyController"
Defensive patterns

Strategy: validation

Validate before calling

import re
assert re.fullmatch(r"[\w.]+:[\w.]+", controller_path), "need 'module.path:ClassName'"

Type guard

def is_valid_import_path(path: str) -> bool:
    if ":" not in path:
        return False
    mod, cls = path.split(":", 1)
    return bool(mod) and bool(cls) and ":" not in cls

Try / catch

try:
    trader.add_controller_from_importable_config(cfg)
except ValueError as e:
    if "must be in format" in str(e):
        raise ConfigError(f"bad import path: {cfg.controller_path}") from e
    raise

Prevention

When it happens

Trigger: Passing `module.path.ClassName` (dots only, no colon), `module.path:` (empty class), `:ClassName` (empty module), `a:b:c` (colon inside class name), or a plain class name to create_python_actor / create_python_strategy via the config path field.

Common situations: Copying a Python dotted path (`nautilus_trader.examples.strategies.ema_cross.EMACross`) into an import path field without appending `:EMACross`; typos with double colons; Windows-style path strings accidentally containing colons.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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