nautechsystems/nautilus_trader · error
canonical trader state must be a string
Error message
canonical trader state must be a string
What it means
The 'trader_state' field inside the canonical 'components' object must be a JSON string (it holds serialized trader state). validate_components throws this when the field is missing, null, or any non-string JSON value. It runs during both from_state construction and from_slice parsing.
Source
Thrown at crates/backtest/src/result.rs:396
"actor_ids",
"exec_algorithm_ids",
"strategy_ids",
"trader_state",
],
"canonical result components",
)?;
for key in ["actor_ids", "exec_algorithm_ids", "strategy_ids"] {
let values = object
.get(key)
.and_then(Value::as_array)
.ok_or_else(|| anyhow::anyhow!("canonical component field '{key}' must be an array"))?;
anyhow::ensure!(
values.iter().all(Value::is_string),
"canonical component field '{key}' must contain strings"
);
}
anyhow::ensure!(
object.get("trader_state").is_some_and(Value::is_string),
"canonical trader state must be a string"
);
Ok(())
}
fn validate_diagnostics(value: &Value) -> anyhow::Result<()> {
let diagnostics = value
.as_array()
.ok_or_else(|| anyhow::anyhow!("canonical diagnostics must be an array"))?;
for diagnostic in diagnostics {
let object = diagnostic
.as_object()
.ok_or_else(|| anyhow::anyhow!("canonical diagnostic must be an object"))?;
validate_fields(object, &["code"], "canonical diagnostic")?;
anyhow::ensure!(
object.get("code").and_then(Value::as_str) == Some("funding-settlement-failed"),
"unsupported canonical diagnostic code"View on GitHub (pinned to 18893faf8b)
Solutions
- Set trader_state to its string-encoded form (e.g. serialized state as a JSON string)
- If state is genuinely empty, use an empty string "" rather than null
- Fix the code path that serializes trader state so it always yields a string
Example fix
// before
"trader_state": null
// after
"trader_state": "{\"initialized\": true}" Defensive patterns
Strategy: validation
Validate before calling
fn trader_state_is_string(c: &serde_json::Value) -> bool {
c.get("trader_state").map_or(false, |v| v.is_string())
} Type guard
fn is_non_null_string(v: &serde_json::Value) -> bool { v.is_string() } Try / catch
match CanonicalResult::from_slice(&bytes) {
Ok(result) => /* use result */,
Err(e) if e.to_string().contains("trader state must be a string") => /* set trader_state to its string-encoded form */,
Err(e) => return Err(e.into()),
} Prevention
- Serialize trader state to its string form before placing it in components
- Use an empty string, never null, when there is no state
- Include trader_state whenever assembling components manually
When it happens
Trigger: from_slice with "components": {..., "trader_state": null} or a nested object; constructing a document where trader state failed to serialize to a string.
Common situations: Omitting trader_state when assembling components by hand; a serializer emitting an object instead of its string-encoded form.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- filter {key:?} contains a non-string value
- Expected JSON object for contract
- Expected JSON array for contracts
- canonical result components must be an object
- canonical component field '{key}' must be an array
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b5b8fd0e7e48bb13.
Report an issue: GitHub.