nautechsystems/nautilus_trader · error

Invalid exec algorithm params

Error message

Invalid exec algorithm params

What it means

When loading an order row, the `exec_algorithm_params` JSONB column is deserialized into `IndexMap<String, String>` and unwrapped with expect(). The panic means the JSON in that column is not an object of string-to-string pairs (wrong JSON shape or types), so the row cannot be hydrated.

Source

Thrown at crates/infrastructure/src/sql/models/orders.rs:1155

        let linked_order_ids = row
            .try_get::<Option<Vec<String>>, _>("linked_order_ids")
            .ok()
            .and_then(|ids| ids.map(|ids| ids.into_iter().map(ClientOrderId::from).collect()));
        let parent_order_id = row
            .try_get::<Option<&str>, _>("parent_order_id")
            .ok()
            .and_then(|x| x.map(ClientOrderId::from));
        let exec_algorithm_id = row
            .try_get::<Option<&str>, _>("exec_algorithm_id")
            .ok()
            .and_then(|x| x.map(ExecAlgorithmId::from));
        let exec_algorithm_params: Option<IndexMap<Ustr, Ustr>> = row
            .try_get::<Option<serde_json::Value>, _>("exec_algorithm_params")
            .ok()
            .and_then(|x| {
                x.map(|x| {
                    serde_json::from_value::<IndexMap<String, String>>(x)
                        .expect("Invalid exec algorithm params")
                })
            })
            .map(|x| {
                x.into_iter()
                    .map(|(k, v)| (Ustr::from(k.as_str()), Ustr::from(v.as_str())))
                    .collect()
            });
        let exec_spawn_id = row
            .try_get::<Option<&str>, _>("exec_spawn_id")
            .ok()
            .and_then(|x| x.map(ClientOrderId::from));
        let tags = tags_from_row(row);
        let init_id = row.try_get::<&str, _>("init_id").map(UUID4::from)?;
        let ts_init = row.try_get::<String, _>("ts_init").map(UnixNanos::from)?;
        let ts_last = row.try_get::<String, _>("ts_last").map(UnixNanos::from)?;

        let snapshot = OrderSnapshot {
            trader_id,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the column: `SELECT id, exec_algorithm_params FROM orders WHERE jsonb_typeof(exec_algorithm_params) <> 'object'` and fix values
  2. Make the writer stringify all param values before storing (json values as strings)
  3. Cast numbers at read time by deserializing into `IndexMap<String, serde_json::Value>` and coercing, or a struct with #[serde(untyped)] handling
  4. Replace expect with error propagation (`?`) so one bad row doesn't abort the whole query

Example fix

// before
serde_json::from_value::<IndexMap<String, String>>(x).expect("Invalid exec algorithm params")
// after
serde_json::from_value::<IndexMap<String, serde_json::Value>>(x)
    .map_err(|e| ModelError::Parse(format!("invalid exec_algorithm_params: {e}")))?
    .into_iter()
    .map(|(k, v)| (Ustr::from(&k), Ustr::from(&v.to_string().trim_matches('"').to_string())))
    .collect()
Defensive patterns

Strategy: validation

Validate before calling

fn valid_exec_params(v: Option<&serde_json::Value>) -> bool {
    v.map(|v| matches!(v, serde_json::Value::Object(m)
        if m.values().all(|x| matches!(x, serde_json::Value::String(_))))).unwrap_or(true)
}

Type guard

fn is_string_map(v: &serde_json::Value) -> bool {
    matches!(v, Value::Object(m) if m.values().all(|x| matches!(x, Value::String(_))))
}

Try / catch

let params = serde_json::from_value::<IndexMap<String, String>>(x)
    .map_err(|e| ModelError::Parse(format!("row {}: invalid exec_algorithm_params: {e}", row_id)))?;

Prevention

When it happens

Trigger: Calling `OrderModel::from_row` where `exec_algorithm_params` contains e.g. a JSON array, a nested object (`{"a": {"b": 1}}`), numeric/boolean values (`{"period": 10}` instead of `"10"`), or invalid JSON entirely.

Common situations: Custom exec-algorithm code writing params with numeric values instead of strings; manual SQL updates to the JSONB column; schema drift between nautilus versions on how params are stored.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — 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/7c674dbd270a958b. Report an issue: GitHub.