nautechsystems/nautilus_trader · error

invalid canonical backtest result JSON: {e}

Error message

invalid canonical backtest result JSON: {e}

What it means

`BacktestResult::from_slice` parses bytes that are supposed to be an already-canonical version-1 backtest result document. If the bytes are not valid JSON at all, serde_json fails and this message wraps the parse error. Canonical results are re-validated, canonicalized, and byte-compared, so input must be exact canonical JSON.

Source

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

    pub position_snapshots: Vec<Position>,
    pub accounts: Vec<AccountAny>,
    pub portfolio_snapshots: Vec<PortfolioSnapshot>,
    pub statistics: PortfolioStatistics,
}

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

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the bytes/file and confirm it is complete valid JSON (e.g. `json.loads` it in Python or `jq . file.json`)
  2. Regenerate the result via the backtest node or `BacktestResult::from_state` instead of from_slice if you only have in-memory state
  3. Strip any BOM, trailing whitespace/garbage, or concatenated JSON before parsing
  4. Ensure the producer wrote with the canonical writer rather than hand-serializing
Defensive patterns

Strategy: validation

Validate before calling

import json
def is_complete_json(path):
    try:
        with open(path, 'rb') as f:
            json.loads(f.read().decode('utf-8-sig'))
        return True
    except Exception:
        return False

Try / catch

match BacktestResult::from_slice(&bytes) {
    Err(e) if e.to_string().contains("invalid canonical backtest result JSON") => {
        eprintln!("not valid JSON: {e:#}"); // fall back to from_state or re-run backtest
    }
    Err(e) => return Err(e),
    Ok(r) => r,
}

Prevention

When it happens

Trigger: Calling `BacktestResult::from_slice` with bytes that are not valid JSON: a truncated file, a log line or text file, binary/protobuf bytes, or JSON produced with trailing garbage after the document.

Common situations: Loading a backtest result file that was truncated by an interrupted write; feeding a non-JSON export (CSV, msgpack) into from_slice; reading a file with a BOM or appended log output.

Understand the failure class

Background: JSON parse error: "Unexpected token" / "not valid JSON" / "failed to parse" — what JSON parsers are really complaining about — this error's family across 45 libraries.

Related errors


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