nautechsystems/nautilus_trader · error
canonical backtest result must be a JSON object
Error message
canonical backtest result must be a JSON object
What it means
`validate_document` (used by both `from_slice` and `from_state`) first requires the canonical backtest result document to be a JSON object at the top level. If the parsed value is an array, string, number, or null, this error is returned before any schema checks.
Source
Thrown at crates/backtest/src/result.rs:307
"total_orders": state.total_orders.to_string(),
"total_positions": state.total_positions.to_string(),
"trader_id": state.trader_id,
},
"schema": CANONICAL_SCHEMA,
"statistics": canonical_statistics(state.statistics),
"summary": state.summary,
});
canonicalize_document(&mut document)?;
validate_document(&document)?;
Ok(Self { document })
}
}
fn validate_document(document: &Value) -> anyhow::Result<()> {
let object = document
.as_object()
.ok_or_else(|| anyhow::anyhow!("canonical backtest result must be a JSON object"))?;
anyhow::ensure!(
object.get("schema").and_then(Value::as_str) == Some(CANONICAL_SCHEMA),
"unsupported canonical backtest result schema"
);
let fields = [
"accounts",
"components",
"diagnostics",
"fills",
"orders",
"portfolio_snapshots",
"position_snapshots",
"positions",
"run",
"schema",
"statistics",
"summary",
];View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure the top-level value is a single JSON object containing `schema`, `accounts`, `components`, etc.
- Unwrap any enclosing array and pass the individual result object
- Regenerate the document with the library's result writer to guarantee the correct top-level shape
- Check the producer code — if it saved the wrong level of the structure, fix the serialization call
Example fix
// before
let bytes = br#"[{"schema":"..."}]"#; // array
BacktestResult::from_slice(bytes)?;
// after
let bytes = br#"{"schema":"...","accounts":[],...}"#; // object
BacktestResult::from_slice(bytes)?; Defensive patterns
Strategy: type-guard
Validate before calling
import json doc = json.load(open(path)) assert isinstance(doc, dict), "result must be a top-level JSON object"
Type guard
fn is_json_object(v: &serde_json::Value) -> bool { v.is_object() } Try / catch
match BacktestResult::from_slice(&bytes) {
Err(e) if e.to_string().contains("must be a JSON object") => {
eprintln!("unexpected top-level JSON type: {e:#}");
}
Err(e) => return Err(e),
Ok(r) => r,
} Prevention
- Always serialize the whole result document, not an inner field or wrapper array
- Type-check the top-level value with `Value::is_object()` before calling from_slice
When it happens
Trigger: Calling `from_slice` with a JSON array (e.g. a list of results), a bare string/number, or null bytes serialized as JSON; calling `from_state` after serializing something other than the result object.
Common situations: Wrapping multiple results in a `[...]` array for batch runs; saving only the `results` inner value or a summary string instead of the whole document; a producer bug that serialized the wrong value.
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 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
- canonical result statistics must be an object
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/1f8736a6370429ac.
Report an issue: GitHub.