nautechsystems/nautilus_trader · error

canonical run configuration ID must be a string or null

Error message

canonical run configuration ID must be a string or null

What it means

This error comes from `validate_run` in the backtest result module, which enforces the schema of the canonical (serialized) backtest result document. The `run_config_id` field of the `run` object must be either a JSON string or JSON null — anything else (number, boolean, missing-then-checked, array, object) is rejected. It exists so downstream consumers of the canonical document can rely on a stable, typed identifier for the run configuration.

Source

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

            "total_positions",
            "trader_id",
        ],
        "canonical result run",
    )?;

    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(())
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Ensure run_config_id is serialized as a string (or explicit null when no config ID exists)
  2. Re-generate the canonical result document with the current version of the library instead of reusing old artifacts
  3. Validate the JSON before passing it to validate_document and coerce non-string IDs to strings with String(value)
  4. Check that no post-processing step (jq, Python script) rewrote the field type

Example fix

// before
{"run": {"run_config_id": 12345, ...}}
// after
{"run": {"run_config_id": "12345", ...}}
Defensive patterns

Strategy: validation

Validate before calling

fn run_config_id_is_valid(run: &serde_json::Value) -> bool {
    run.get("run_config_id")
        .is_some_and(|v| v.is_null() || v.is_string())
}

Type guard

fn is_string_or_null(v: &serde_json::Value) -> bool {
    v.is_null() || v.is_string()
}

Try / catch

match validate_document(&doc) {
    Err(e) if e.to_string().contains("run configuration ID") => {
        // coerce run.run_config_id to string or null and retry
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling `validate_document` on a canonical result JSON whose `run.run_config_id` is a non-string, non-null JSON value (e.g. an integer, an object, or an array). This happens when the document was produced by an older/other serializer or hand-edited rather than emitted by this library's canonical writer.

Common situations: Migrating results produced by a previous schema version where run_config_id was encoded as a number; hand-crafting or post-processing result JSON with a script that coerces the ID to a number; loading fixture files from a different codebase version.

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