nautechsystems/nautilus_trader · error

canonical trader ID must be a string

Error message

canonical trader ID must be a string

What it means

`validate_run` requires the `trader_id` field of the canonical run object to be a JSON string. This error is thrown when the field exists but holds another JSON type (number, null, object, array) or is a non-string value. Trader IDs identify the trading node instance that produced the backtest result, and the canonical format mandates a string so consumers can parse them into the domain TraderId type.

Source

Thrown at crates/backtest/src/result.rs:458

    for key in [
        "iterations",
        "total_events",
        "total_orders",
        "total_positions",
    ] {
        validate_unsigned_decimal(object.get(key).expect("validated field"), key, false)?;
    }

    for key in ["backtest_start_ns", "backtest_end_ns"] {
        validate_unsigned_decimal(object.get(key).expect("validated field"), key, true)?;
    }
    anyhow::ensure!(
        object
            .get("run_config_id")
            .is_some_and(|value| value.is_null() || value.is_string()),
        "canonical run configuration ID must be a string or null"
    );
    anyhow::ensure!(
        object.get("trader_id").is_some_and(Value::is_string),
        "canonical trader ID must be a string"
    );
    anyhow::ensure!(
        matches!(
            object.get("outcome").and_then(Value::as_str),
            Some("completed" | "failed" | "incomplete" | "stopped")
        ),
        "unsupported canonical run outcome"
    );
    Ok(())
}

fn validate_unsigned_decimal(value: &Value, field: &str, nullable: bool) -> anyhow::Result<()> {
    if nullable && value.is_null() {
        return Ok(());
    }
    let value = value

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Set run.trader_id to the trader ID as a JSON string (e.g. "TRADER-001")
  2. Regenerate the canonical document using the library's own result writer
  3. Pre-validate the JSON and convert non-string trader IDs to strings before validation
  4. Verify the source of the document matches the current canonical schema version

Example fix

// before
{"run": {"trader_id": null, ...}}
// after
{"run": {"trader_id": "TRADER-001", ...}}
Defensive patterns

Strategy: validation

Validate before calling

fn trader_id_is_valid(run: &serde_json::Value) -> bool {
    run.get("trader_id").is_some_and(serde_json::Value::is_string)
}

Type guard

fn is_nonempty_string(v: &serde_json::Value) -> bool {
    v.as_str().map_or(false, |s| !s.is_empty())
}

Try / catch

match validate_document(&doc) {
    Err(e) if e.to_string().contains("trader ID must be a string") => {
        // stringify run.trader_id and retry
    }
    other => other,
}

Prevention

When it happens

Trigger: Passing a canonical result document to `validate_document` (via `validate_run`) where `run.trader_id` is not a JSON string — e.g. null, a number, or a nested object. Typically the document was not produced by this library's canonical serializer.

Common situations: Hand-built result JSON for testing; results migrated from an older format with a differently-typed trader identifier; a serialization tool that drops quotes around identifiers; fixtures copied from another schema.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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