nautechsystems/nautilus_trader · error
serialized position adjustment was not an object
Error message
serialized position adjustment was not an object
What it means
Raised by `patch_position_adjustment` when the JSON produced from a `PositionAdjusted` event is not a JSON object (`Value::Object`), so its `quantity_change` key cannot be patched with a stringified decimal. The canonicalizer expects every serialized position-adjustment event to deserialize into a map; array/string/number encodings indicate the event type's serde shape changed.
Source
Thrown at crates/backtest/src/result.rs:752
}
}
fn patch_order_event_decimals(event: &OrderEventAny, value: &mut Value) -> anyhow::Result<()> {
if let OrderEventAny::Initialized(initialized) = event {
let payload = variant_payload_mut(value)?;
set_decimal(payload, "limit_offset", initialized.limit_offset);
set_decimal(payload, "trailing_offset", initialized.trailing_offset);
}
Ok(())
}
fn patch_position_adjustment(
adjustment: &PositionAdjusted,
value: &mut Value,
) -> anyhow::Result<()> {
let object = value
.as_object_mut()
.ok_or_else(|| anyhow::anyhow!("serialized position adjustment was not an object"))?;
set_decimal(object, "quantity_change", adjustment.quantity_change);
Ok(())
}
fn variant_payload_mut(value: &mut Value) -> anyhow::Result<&mut Map<String, Value>> {
let variant = value
.as_object_mut()
.and_then(|object| object.values_mut().next())
.and_then(Value::as_object_mut)
.ok_or_else(|| anyhow::anyhow!("serialized enum variant was not an object"))?;
Ok(variant)
}
fn canonical_value<T: Serialize>(source: &T) -> anyhow::Result<Value> {
let mut value = serde_json::to_value(source)?;
canonicalize_value(&mut value)?;
Ok(value)
}View on GitHub (pinned to 18893faf8b)
Solutions
- Keep `PositionAdjusted` serialized as a JSON object (struct or externally-tagged struct variant), not a tuple/newtype variant.
- Inspect the produced JSON with `serde_json::to_string_pretty` to see the actual shape.
- Update `patch_position_adjustment` if the representation legitimately changed, adjusting how it descends into the variant payload (use `variant_payload_mut`).
- Rebuild and rerun backtest result tests to refresh golden fixtures.
Example fix
// before: PositionAdjusted(Quantity) // serializes to a bare number
// after:
#[derive(Serialize)]
struct PositionAdjusted { quantity_change: Quantity } Defensive patterns
Strategy: type-guard
Validate before calling
let v = serde_json::to_value(&event)?; assert!(v.is_object(), "PositionAdjusted must serialize to an object");
Type guard
fn is_object(v: &serde_json::Value) -> bool { v.is_object() } Prevention
- Keep event types as struct variants with named fields.
- Dump serialized events to JSON in unit tests to catch representation drift early.
When it happens
Trigger: Canonicalizing a `PositionAdjusted` event whose `serde_json::to_value` yields a non-object — e.g. a newtype/enum variant serialized as an array or tuple because the struct gained tuple representation, or a custom `Serialize` impl emitting a bare string.
Common situations: Domain event structs changed from struct variants to tuple variants in an enum; developers adding fields to `PositionAdjusted` while switching its serde representation; stale fixtures from an older schema.
Understand the failure class
Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.
Related errors
- Python object has no to_json() method or __dict__ attribute
- Failed to serialize the deployment manifest
- invalid canonical backtest result JSON: {e}
- canonical backtest result violates the version 1 encoding ru
- canonical backtest result bytes do not use the canonical enc
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/62f9126bbe956e73.
Report an issue: GitHub.