nautechsystems/nautilus_trader · error · anyhow::Error

Invalid filter_callable path {filter_callable:?}; expected m

Error message

Invalid filter_callable path {filter_callable:?}; expected module.callable

What it means

The user-supplied filter_callable string must be in 'module.callable' form (e.g. 'my.filters.skip_fx'). This error is thrown by passes_filter_callable when the string contains no '.' so rsplit_once returns None, making it impossible to determine which module and function to import.

Source

Thrown at crates/adapters/interactive_brokers/src/providers/instruments.rs:1785

        }

        should_update
    }

    fn passes_filter_callable(&self, instrument: &InstrumentAny) -> anyhow::Result<bool> {
        let Some(filter_callable) = self.config.filter_callable.as_deref() else {
            return Ok(true);
        };

        #[cfg(feature = "python")]
        {
            use nautilus_model::python::instruments::instrument_any_to_pyobject;
            use pyo3::{prelude::*, types::PyModule};

            Python::attach(|py| {
                let (module_name, callable_name) =
                    filter_callable.rsplit_once('.').ok_or_else(|| {
                        anyhow::anyhow!(
                            "Invalid filter_callable path {filter_callable:?}; expected module.callable"
                        )
                    })?;
                let callable = PyModule::import(py, module_name)
                    .map_err(|e| anyhow::anyhow!("Failed to import {module_name}: {e}"))?
                    .getattr(callable_name)
                    .map_err(|e| anyhow::anyhow!("Failed to resolve {filter_callable}: {e}"))?;
                let py_instrument = instrument_any_to_pyobject(py, instrument.clone())
                    .map_err(|e| anyhow::anyhow!("Failed to convert instrument to Python: {e}"))?;
                callable
                    .call1((py_instrument,))
                    .and_then(|result| result.extract::<bool>())
                    .map_err(|e| anyhow::anyhow!("filter_callable {filter_callable} failed: {e}"))
            })
        }

        #[cfg(not(feature = "python"))]
        {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Change the value to a fully qualified dotted path: 'package.module.function'
  2. Confirm the string comes from config parsing and is not truncated
  3. Add a startup-time format check so a bad path fails fast at config load rather than mid-processing

Example fix

// before
filter_callable = "skip_fx"
// after
filter_callable = "mypackage.filters.skip_fx"
Defensive patterns

Strategy: validation

Validate before calling

def validate_filter_callable(path: str) -> str:
    if "." not in path or not all(path.split('.')):
        raise ValueError(f"filter_callable must be 'module.callable': {path!r}")
    return path

Type guard

function isDottedPath(s) { return typeof s === 'string' && /^[A-Za-z_][\w.]*\.[A-Za-z_]\w*$/.test(s); }

Try / catch

try:
    process_contract_detail(details, filter_callable)
except Exception as e:
    if "Invalid filter_callable path" in str(e):
        raise ConfigError(f"bad filter_callable: {e}") from e
    raise

Prevention

When it happens

Trigger: Passing a filter_callable like 'skip_fx' or '' (no dot) into contract detail processing instead of a dotted module path.

Common situations: Typo omitting the module prefix; passing a bare function name assuming the library resolves it in the current namespace; config value read from a TOML/YAML file missing the module part.

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