nautechsystems/nautilus_trader · error · anyhow::Error

config_path must be in format 'module.path:ClassName', was {

Error message

config_path must be in format 'module.path:ClassName', was {config_path}

What it means

create_config_instance imports a config class from Python by splitting config_path on ':'. NautilusTrader importable config paths must have exactly two segments: 'module.path:ClassName'. Any string without exactly one colon (zero colons, or two or more) is rejected.

Source

Thrown at crates/backtest/src/python/node.rs:508

        };
        Ok(component.unbind())
    })
    .map_err(to_pyruntime_err)
}

pub(crate) fn create_config_instance<'py>(
    py: Python<'py>,
    config_path: &str,
    config: &HashMap<String, serde_json::Value>,
) -> anyhow::Result<Option<Bound<'py, PyAny>>> {
    if config_path.is_empty() && config.is_empty() {
        log::debug!("No config_path or empty config, using None");
        return Ok(None);
    }

    let config_parts: Vec<&str> = config_path.split(':').collect();
    if config_parts.len() != 2 {
        anyhow::bail!("config_path must be in format 'module.path:ClassName', was {config_path}");
    }
    let (config_module_name, config_class_name) = (config_parts[0], config_parts[1]);

    log::debug!(
        "Importing config class from module: {config_module_name} class: {config_class_name}"
    );

    let config_module = py
        .import(config_module_name)
        .map_err(|e| anyhow::anyhow!("Failed to import config module {config_module_name}: {e}"))?;
    let config_class = config_module
        .getattr(config_class_name)
        .map_err(|e| anyhow::anyhow!("Failed to get config class {config_class_name}: {e}"))?;

    // Convert config dict to Python dict
    let py_dict = PyDict::new(py);

    for (key, value) in config {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Rewrite the path to exactly 'module.path:ClassName' — dots in the module, one colon before the class
  2. Strip any extra ':key' suffixes; pass extra data through the config object itself, not the path
  3. Log/inspect the config_path value before calling and validate it with a regex like r'^[\w.]+:[\w]+$'

Example fix

// before
config_path = "nautilus_trader.examples.strategies.my_strategy:MyStrategyConfig:extra"
// after
config_path = "nautilus_trader.examples.strategies.my_strategy:MyStrategyConfig"
Defensive patterns

Strategy: validation

Validate before calling

import re
def validate_config_path(path):
    if not isinstance(path, str) or not re.fullmatch(r"[\w.]+:[\w]+", path):
        raise ValueError(f"config_path must be 'module.path:ClassName', was {path!r}")
    return path

Type guard

def is_valid_config_path(path) -> bool:
    return isinstance(path, str) and path.count(':') == 1 and all(path.split(':'))

Try / catch

try:
    component = create_importable_component(config_path)
except ValueError as e:
    log.error("Bad config_path %r: %s", config_path, e)

Prevention

When it happens

Trigger: Passing a config path like 'my_module.MyConfig' (dot instead of colon), 'my.module.Config:Subkey:Extra' (extra colons), or an empty/whitespace path fragment via create_importable_component.

Common situations: Copy-pasting a fully-qualified Python class name without converting the last dot to a colon; programmatic config generation producing module:Class:extra; typos in YAML/JSON component config.

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/34078d808ee61bc3. Report an issue: GitHub.