nautechsystems/nautilus_trader · error

canonical backtest result violates the version 1 encoding ru

Error message

canonical backtest result violates the version 1 encoding rules

What it means

`from_slice` validates that the parsed document is already in canonical form: it clones, canonicalizes, and requires the canonicalized value to equal the original document. If re-canonicalization changes anything (key order is fine since JSON objects compare by content, but e.g. number formatting/duplicates/extra or missing normalized fields differ), this error is thrown. The input must follow the version-1 encoding rules exactly.

Source

Thrown at crates/backtest/src/result.rs:193

}

impl CanonicalBacktestResult {
    /// Decodes canonical result bytes and verifies their envelope, normalization, and exact
    /// encoding.
    ///
    /// Inner records retain their NautilusTrader model serialization and are compared as semantic
    /// content. Producers must use the version 1 writer rather than construct records independently.
    ///
    /// # Errors
    ///
    /// Returns an error if the bytes are not a canonical version 1 result.
    pub fn from_slice(bytes: &[u8]) -> anyhow::Result<Self> {
        let document: Value = serde_json::from_slice(bytes)
            .map_err(|e| anyhow::anyhow!("invalid canonical backtest result JSON: {e}"))?;
        validate_document(&document)?;
        let mut normalized = document.clone();
        canonicalize_document(&mut normalized)?;
        anyhow::ensure!(
            normalized == document,
            "canonical backtest result violates the version 1 encoding rules"
        );
        let canonical = serde_json::to_vec(&normalized)?;
        anyhow::ensure!(
            canonical == bytes,
            "canonical backtest result bytes do not use the canonical encoding"
        );
        Ok(Self { document })
    }

    /// Returns the canonical compact UTF-8 JSON bytes without trailing data.
    ///
    /// # Errors
    ///
    /// Returns an error if the in-memory document cannot be serialized.
    pub fn to_bytes(&self) -> anyhow::Result<Vec<u8>> {
        Ok(serde_json::to_vec(&self.document)?)

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Regenerate the result bytes with the library's canonical serializer instead of hand-editing or re-serializing with another tool
  2. Round-trip: build a `BacktestResult` from state (`from_state`) and re-emit it canonically
  3. Compare your document against `canonicalize_document` output to find the diverging field and fix its encoding
  4. Ensure the result was produced by the same library version that defines CANONICAL_SCHEMA v1 rules

Example fix

// before
let bytes = serde_json::to_vec(&value)?; // non-canonical number formatting
BacktestResult::from_slice(&bytes)?;
// after
let result = BacktestResult::from_state(state)?; // canonical by construction
let bytes = result.to_bytes()?; // then from_slice succeeds
Defensive patterns

Strategy: validation

Validate before calling

// Verify document survives canonicalization unchanged
let mut normalized = document.clone();
canonicalize_document(&mut normalized)?;
assert_eq!(normalized, document, "document is not canonically encoded");

Try / catch

match BacktestResult::from_slice(&bytes) {
    Err(e) if e.to_string().contains("violates the version 1 encoding rules") => {
        eprintln!("non-canonical content, regenerate via canonical writer");
    }
    Err(e) => return Err(e),
    Ok(r) => r,
}

Prevention

When it happens

Trigger: Feeding a semantically valid but non-canonical result document: fields added/removed/reordered in a way canonicalization would alter, numbers serialized differently than the canonical encoder would (e.g. `1.0` vs `1`, exponent forms), or a hand-edited result file.

Common situations: Manually editing a saved backtest result; re-serializing a result with a different JSON library that formats numbers differently; results produced by an older or newer version of the canonicalizer.

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


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/4d33acfcca4a5be1. Report an issue: GitHub.