nautechsystems/nautilus_trader · error
canonical result components must be an object
Error message
canonical result components must be an object
What it means
The 'components' section of a canonical result must be a JSON object with keys actor_ids, exec_algorithm_ids, strategy_ids, and trader_state. This error is thrown when the value is null, an array, a string, or any non-object JSON value. It runs during validate_document for both parsed and constructed documents.
Source
Thrown at crates/backtest/src/result.rs:374
fn validate_fields(
object: &Map<String, Value>,
expected: &[&str],
context: &str,
) -> anyhow::Result<()> {
let actual = object.keys().map(String::as_str).collect::<BTreeSet<_>>();
let expected = expected.iter().copied().collect::<BTreeSet<_>>();
anyhow::ensure!(
actual == expected,
"{context} fields do not match the version 1 schema"
);
Ok(())
}
fn validate_components(value: &Value) -> anyhow::Result<()> {
let object = value
.as_object()
.ok_or_else(|| anyhow::anyhow!("canonical result components must be an object"))?;
validate_fields(
object,
&[
"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),View on GitHub (pinned to 18893faf8b)
Solutions
- Change components to an object: {"actor_ids": [...], "exec_algorithm_ids": [...], "strategy_ids": [...], "trader_state": "..."}
- Check that the serialization code that builds 'components' emits a map, not a list
- Regenerate the document with the library's canonical writer
Example fix
// before
"components": []
// after
"components": {"actor_ids": [], "exec_algorithm_ids": [], "strategy_ids": ["S-1"], "trader_state": "..."} Defensive patterns
Strategy: validation
Validate before calling
fn components_is_object(doc: &serde_json::Value) -> bool {
doc.get("components").map_or(false, |v| v.is_object())
} Type guard
fn is_object(v: &serde_json::Value) -> bool { v.is_object() } Try / catch
match CanonicalResult::from_slice(&bytes) {
Ok(result) => /* use result */,
Err(e) if e.to_string().contains("components must be an object") => /* convert components to an object and retry */,
Err(e) => return Err(e.into()),
} Prevention
- Emit components as a map with the four required keys, never as a list
- Use [] arrays for the ID fields and a string for trader_state
- Regenerate documents with the library instead of assembling JSON by hand
When it happens
Trigger: from_slice on JSON with "components": [] or "components": null, or constructing a document whose components entry is not a JSON object.
Common situations: Hand-editing or programmatically assembling result JSON and serializing components as a list or omitting it to null; a tool from another schema emitting components as an array.
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
- canonical diagnostic must be an object
- canonical result run must be an object
- filter {key:?} contains a non-string value
- Expected JSON object for contract
- Expected JSON array for contracts
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/a9cea443d374b151.
Report an issue: GitHub.