{"record":{"id":"53252c0165bf7101","repo":"nautechsystems/nautilus_trader","slug":"canonical-run-field-field-must-be-a-decimal-st","errorCode":null,"errorMessage":"canonical run field '{field}' must be a decimal string","messagePattern":"canonical run field '(.+?)' must be a decimal string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/backtest/src/result.rs","lineNumber":478,"sourceCode":"        \"canonical trader ID must be a string\"\n    );\n    anyhow::ensure!(\n        matches!(\n            object.get(\"outcome\").and_then(Value::as_str),\n            Some(\"completed\" | \"failed\" | \"incomplete\" | \"stopped\")\n        ),\n        \"unsupported canonical run outcome\"\n    );\n    Ok(())\n}\n\nfn validate_unsigned_decimal(value: &Value, field: &str, nullable: bool) -> anyhow::Result<()> {\n    if nullable && value.is_null() {\n        return Ok(());\n    }\n    let value = value\n        .as_str()\n        .ok_or_else(|| anyhow::anyhow!(\"canonical run field '{field}' must be a decimal string\"))?;\n    anyhow::ensure!(\n        value == \"0\"\n            || (!value.is_empty()\n                && !value.starts_with('0')\n                && value.bytes().all(|byte| byte.is_ascii_digit())),\n        \"canonical run field '{field}' is not a canonical unsigned decimal\"\n    );\n    Ok(())\n}\n\nfn validate_statistics(value: &Value) -> anyhow::Result<()> {\n    let object = value\n        .as_object()\n        .ok_or_else(|| anyhow::anyhow!(\"canonical result statistics must be an object\"))?;\n    validate_fields(\n        object,\n        &[\"general\", \"pnls\", \"returns\", \"returns_series\"],\n        \"canonical result statistics\",","sourceCodeStart":460,"sourceCodeEnd":496,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/backtest/src/result.rs#L460-L496","documentation":"`validate_unsigned_decimal` requires canonical run numeric fields (iterations, total_events, total_orders, total_positions) and timestamp fields (backtest_start_ns, backtest_end_ns) to be encoded as JSON strings containing decimal digits. This error is thrown when the field's JSON value is not a string at all (e.g. a number or null for a non-nullable field). The library encodes large integers as strings to preserve exact values.","triggerScenarios":"Validating a canonical result where a run field like \"iterations\", \"total_orders\", or \"backtest_start_ns\" is a JSON number (e.g. 42 instead of \"42\") or null where null is not allowed. Happens with JSON produced by a default serializer that emits raw numbers.","commonSituations":"Results serialized by generic JSON tools rather than the canonical writer; fixtures where the ns timestamps were written as numbers; null backtest_start_ns/backtest_end_ns passed where the field is required non-null (nullable=false for iterations/total_* fields).","solutions":["Encode the field as a decimal string, e.g. \"42\" instead of 42","Regenerate the document via the library's canonical result writer (which applies string encoding)","Pre-transform the JSON to stringify numeric fields before validation","Check the nullable flag: null is only allowed for backtest_start_ns/backtest_end_ns"],"exampleFix":"// before\n{\"run\": {\"iterations\": 100, \"backtest_start_ns\": null}}\n// after\n{\"run\": {\"iterations\": \"100\", \"backtest_start_ns\": \"1720000000000000000\"}}","handlingStrategy":"validation","validationCode":"fn run_decimal_fields_are_strings(run: &serde_json::Value) -> bool {\n    [\"iterations\", \"total_events\", \"total_orders\", \"total_positions\",\n     \"backtest_start_ns\", \"backtest_end_ns\"]\n        .iter()\n        .all(|k| run.get(*k).map_or(false, serde_json::Value::is_string))\n}","typeGuard":"fn is_decimal_string(v: &serde_json::Value) -> bool {\n    v.as_str().map_or(false, |s| !s.is_empty() && s.bytes().all(|b| b.is_ascii_digit()))\n}","tryCatchPattern":"match validate_document(&doc) {\n    Err(e) if e.to_string().contains(\"must be a decimal string\") => {\n        // stringify numeric run fields and retry\n    }\n    other => other,\n}","preventionTips":["Encode all canonical integers as JSON strings, never raw numbers","Use the canonical writer's string-encoding helpers (e.g. nanos.to_string())","Disable default numeric serialization when exporting results","Test fixtures with the validator before shipping them"],"tags":["json","validation","type-mismatch","backtest"],"backgroundTag":"type-mismatch","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}