nautechsystems/nautilus_trader · error

Invalid `external_order_instrument_ids` type: {e}

Error message

Invalid `external_order_instrument_ids` type: {e}

What it means

Raised while extracting the external_order_instrument_ids attribute from a strategy config: the value is neither a list of InstrumentId objects nor a list of strings parseable as InstrumentId. The node accepts both forms and rejects anything else.

Source

Thrown at crates/live/src/python/node.rs:1911

fn extract_external_order_instrument_ids_config_attr(
    config_obj: &Bound<'_, PyAny>,
) -> anyhow::Result<Option<Vec<InstrumentId>>> {
    let Ok(claims) = config_obj.getattr("external_order_instrument_ids") else {
        return Ok(None);
    };

    if claims.is_none() {
        return Ok(None);
    }

    if let Ok(claims) = claims.extract::<Vec<InstrumentId>>() {
        return Ok(Some(claims));
    }

    let claim_strings = claims
        .extract::<Vec<String>>()
        .map_err(|e| anyhow::anyhow!("Invalid `external_order_instrument_ids` type: {e}"))?;
    let claims = claim_strings
        .into_iter()
        .map(|claim| {
            InstrumentId::from_str(&claim).map_err(|e| {
                anyhow::anyhow!(
                    "Invalid `external_order_instrument_ids` instrument ID {claim}: {e}"
                )
            })
        })
        .collect::<anyhow::Result<Vec<_>>>()?;

    Ok(Some(claims))
}

#[cfg(feature = "examples")]
type BuiltinActorRegister = for<'py> fn(&mut LiveNode, &Bound<'py, PyAny>) -> PyResult<()>;

#[cfg(feature = "examples")]

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure the value is a list, e.g. ["AAPL.NASDAQ", "MSFT.NASDAQ"]
  2. Use fully qualified instrument IDs with venue (InstrumentId::from_str parses venue.symbol); fix IDs flagged by the follow-up 'instrument ID' error
  3. In Python configs pass list(instrument_ids), not a tuple or single string
  4. Validate IDs up front with InstrumentId.from_str in a sanity check before launching the node

Example fix

// before
config["external_order_instrument_ids"] = "AAPL.NASDAQ,MSFT.NASDAQ"
// after
config["external_order_instrument_ids"] = ["AAPL.NASDAQ", "MSFT.NASDAQ"]
Defensive patterns

Strategy: validation

Validate before calling

from nautilus_trader.model.identifiers import InstrumentId
ids = config["external_order_instrument_ids"]
assert isinstance(ids, list), "must be a list"
parsed = [InstrumentId.from_str(i) for i in ids]  # raises on bad IDs

Type guard

def valid_external_ids(value) -> bool:
    from nautilus_trader.model.identifiers import InstrumentId
    if not isinstance(value, list):
        return False
    try:
        [InstrumentId.from_str(i) for i in value]
        return True
    except Exception:
        return False

Try / catch

try:
    node.add_strategy_from_config(cfg)
except Exception as e:
    if "external_order_instrument_ids" in str(e):
        raise ValueError("external_order_instrument_ids must be a list of 'SYMBOL.VENUE' strings") from e
    raise

Prevention

When it happens

Trigger: Setting external_order_instrument_ids in the strategy config to a wrong shape: a single string instead of a list, a list of non-instrument strings (typos like 'AAPL' without venue, or 'EUR/USD' when venue format is required), a set, tuple, or None-adjacent non-list type.

Common situations: Hand-editing YAML/JSON configs with a comma-joined string; missing the venue suffix ("AAPL" instead of "AAPL.NASDAQ"); passing a tuple from Python code where a list is expected by the extractor.

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