nautechsystems/nautilus_trader · error
canonical result statistics must be an object
Error message
canonical result statistics must be an object
What it means
`validate_statistics` checks the `statistics` section of a canonical backtest result. The whole section must be a JSON object; this error is thrown when it is instead an array, string, number, boolean, or null. The statistics block is expected to contain the sub-objects general/pnls/returns plus the returns_series array, so a non-object makes the document unusable.
Source
Thrown at crates/backtest/src/result.rs:492
return Ok(());
}
let value = value
.as_str()
.ok_or_else(|| anyhow::anyhow!("canonical run field '{field}' must be a decimal string"))?;
anyhow::ensure!(
value == "0"
|| (!value.is_empty()
&& !value.starts_with('0')
&& value.bytes().all(|byte| byte.is_ascii_digit())),
"canonical run field '{field}' is not a canonical unsigned decimal"
);
Ok(())
}
fn validate_statistics(value: &Value) -> anyhow::Result<()> {
let object = value
.as_object()
.ok_or_else(|| anyhow::anyhow!("canonical result statistics must be an object"))?;
validate_fields(
object,
&["general", "pnls", "returns", "returns_series"],
"canonical result statistics",
)?;
for key in ["general", "pnls", "returns"] {
anyhow::ensure!(
object.get(key).is_some_and(Value::is_object),
"canonical statistics field '{key}' must be an object"
);
}
anyhow::ensure!(
object.get("returns_series").is_some_and(Value::is_array),
"canonical returns series must be an array"
);
Ok(())
}View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the statistics section is a JSON object with keys general, pnls, returns, and returns_series
- Regenerate the canonical result with the current library version
- Migrate/transform old-format statistics arrays into the object shape before validation
- Check that no post-processing step replaced or nulled the statistics field
Example fix
// before
{"statistics": []}
// after
{"statistics": {"general": {}, "pnls": {}, "returns": {}, "returns_series": []}} Defensive patterns
Strategy: validation
Validate before calling
fn statistics_shape_is_valid(doc: &serde_json::Value) -> bool {
doc.get("statistics").map_or(false, serde_json::Value::is_object)
} Type guard
fn is_object(v: &serde_json::Value) -> bool { v.is_object() } Try / catch
match validate_document(&doc) {
Err(e) if e.to_string().contains("statistics must be an object") => {
// rebuild statistics section as an object and retry
}
other => other,
} Prevention
- Emit statistics as an object with the required keys general/pnls/returns/returns_series
- Default empty statistics to {} rather than null or []
- Validate documents produced by older formats before migration
- Keep a schema test that serializes a sample result and validates it
When it happens
Trigger: Passing a canonical result document to validate_document where `statistics` is not a JSON object — e.g. an empty array [], a null, or a string. Typically from a mismatched schema version or a document whose statistics section was replaced/corrupted.
Common situations: Old result formats where statistics was an array of metrics; documents assembled by hand where statistics was omitted and defaulted to null; a script that assigned the wrong container type when aggregating stats.
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
- canonical backtest result must be a JSON object
- canonical result field '{key}' must be an array
- canonical result summary must be an object
- canonical run configuration ID must be a string or null
- canonical trader ID must be a string
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/026a27964c331764.
Report an issue: GitHub.