nautechsystems/nautilus_trader · error
canonical projection contains an unencoded floating-point va
Error message
canonical projection contains an unencoded floating-point value
What it means
Raised by `stringify_numbers` during canonical-value preparation. Any `Value::Number` that is neither i64 nor u64 is a float, which would leak nondeterministic `f64` formatting into the canonical projection; the code requires all numerics to be integral before encoding them as strings.
Source
Thrown at crates/backtest/src/result.rs:993
"venue_order_ids" => Some(IdentityClass::VenueOrder),
_ => None,
}
}
fn stringify_numbers(value: &mut Value) -> anyhow::Result<()> {
match value {
Value::Array(values) => {
for value in values {
stringify_numbers(value)?;
}
}
Value::Object(object) => {
for value in object.values_mut() {
stringify_numbers(value)?;
}
}
Value::Number(number) => {
anyhow::ensure!(
number.is_i64() || number.is_u64(),
"canonical projection contains an unencoded floating-point value"
);
*value = Value::String(number.to_string());
}
_ => {}
}
Ok(())
}
fn first_divergence(
expected: &Value,
actual: &Value,
path: String,
) -> Option<CanonicalResultDivergence> {
match (expected, actual) {
(Value::Object(expected), Value::Object(actual)) => {
let keys = expectedView on GitHub (pinned to 18893faf8b)
Solutions
- Replace raw `f64` fields with domain types (`Price`, `Quantity`, `Money`) or `Decimal` so they serialize as strings.
- Use serde stringification (e.g. `#[serde(with = ...)]` or `as_raw().to_string()`) for any numeric that must survive canonicalization.
- Reproduce by running the failing backtest and dumping the canonical JSON to find the offending key.
- Add/refresh unit tests asserting no floats appear in canonical output.
Example fix
// before pub quantity: f64, // after pub quantity: Quantity, // serializes as string "100"
Defensive patterns
Strategy: validation
Validate before calling
let v = serde_json::to_value(&output)?;
fn has_floats(v: &serde_json::Value) -> bool {
match v {
Value::Number(n) => !(n.is_i64() || n.is_u64()),
Value::Array(a) => a.iter().any(has_floats),
Value::Object(o) => o.values().any(has_floats),
_ => false,
}
} Prevention
- Use Price/Quantity/Money domain types instead of f64 in output structs.
- Enforce string-encoded decimals in serde with custom (de)serializers.
When it happens
Trigger: A serialized order/position/account/event value contains an `f64`-backed JSON number — i.e. some field (price, quantity, money) was serialized from `f64` instead of a Decimal/int, or serde emitted a float during canonicalization.
Common situations: Using plain `f64` fields instead of the project's `Price`/`Quantity`/`Money` types in a struct that flows into backtest result output; calling `serde_json::to_value` on types that don't implement decimal-as-string serialization.
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
- Cap'n Proto decoding requires the `capnp` feature
- SBE decoding requires the `sbe` feature
- SBE encoding is not supported for Redis cache payloads
- Cap'n Proto encoding is not supported for Redis cache payloa
- Python object has no to_json() method or __dict__ attribute
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/7fef421c80ab5baf.
Report an issue: GitHub.