nautechsystems/nautilus_trader · error

unsupported canonical run outcome

Error message

unsupported canonical run outcome

What it means

The canonical run document's `outcome` field is restricted to the closed set of strings "completed", "failed", "incomplete", or "stopped`. `validate_run` throws this error when `outcome` is any other string, a non-string value, or absent (though missing fields are normally caught earlier by validate_fields). It guarantees consumers can exhaustively match on run outcomes.

Source

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

        "total_positions",
    ] {
        validate_unsigned_decimal(object.get(key).expect("validated field"), key, false)?;
    }

    for key in ["backtest_start_ns", "backtest_end_ns"] {
        validate_unsigned_decimal(object.get(key).expect("validated field"), key, true)?;
    }
    anyhow::ensure!(
        object
            .get("run_config_id")
            .is_some_and(|value| value.is_null() || value.is_string()),
        "canonical run configuration ID must be a string or null"
    );
    anyhow::ensure!(
        object.get("trader_id").is_some_and(Value::is_string),
        "canonical trader ID must be a string"
    );
    anyhow::ensure!(
        matches!(
            object.get("outcome").and_then(Value::as_str),
            Some("completed" | "failed" | "incomplete" | "stopped")
        ),
        "unsupported canonical run outcome"
    );
    Ok(())
}

fn validate_unsigned_decimal(value: &Value, field: &str, nullable: bool) -> anyhow::Result<()> {
    if nullable && value.is_null() {
        return Ok(());
    }
    let value = value
        .as_str()
        .ok_or_else(|| anyhow::anyhow!("canonical run field '{field}' must be a decimal string"))?;
    anyhow::ensure!(
        value == "0"

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Map your outcome to one of the supported values: completed, failed, incomplete, stopped
  2. Regenerate the result with the current library version so the canonical writer emits a valid outcome
  3. Update translation/import code to normalize outcome strings before validation
  4. Check the schema version of the input document against what this validator expects

Example fix

// before
{"run": {"outcome": "success", ...}}
// after
{"run": {"outcome": "completed", ...}}
Defensive patterns

Strategy: validation

Validate before calling

const OUTCOMES: &[&str] = &["completed", "failed", "incomplete", "stopped"];
fn outcome_is_valid(run: &serde_json::Value) -> bool {
    run.get("outcome")
        .and_then(serde_json::Value::as_str)
        .map_or(false, |o| OUTCOMES.contains(&o))
}

Type guard

fn is_supported_outcome(v: &serde_json::Value) -> bool {
    matches!(v.as_str(), Some("completed" | "failed" | "incomplete" | "stopped"))
}

Try / catch

match validate_document(&doc) {
    Err(e) if e.to_string().contains("canonical run outcome") => {
        // normalize outcome string to the supported set and retry
    }
    other => other,
}

Prevention

When it happens

Trigger: Validating a canonical result where `run.outcome` is a value outside the allowed enum — e.g. "ok", "success", "aborted", an empty string, or a non-string such as null/number. Common when documents come from a different version or custom tooling that uses different outcome vocabulary.

Common situations: Custom backtest runners writing their own outcome strings; schema drift after the outcome enum was tightened in this version; translating results from another system (e.g. "success" vs "completed"); hand-edited fixtures.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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