nautechsystems/nautilus_trader · error

Failed to serialize account event: {e}

Error message

Failed to serialize account event: {e}

What it means

add_account serializes the whole AccountState event to serde_json::Value and then picks out the "balances" and "margins" keys. This error means serde_json::to_value(&account_event) itself returned Err. Since AccountState derives Serialize, this is rare and typically indicates a custom or poisoned serialization path (e.g. an f64 NaN/Infinity in an override, or a broken custom Serialize impl for a contained type).

Source

Thrown at crates/infrastructure/src/sql/queries.rs:1110

        pool: &PgPool,
        updated: bool,
        account_event: AccountState,
    ) -> anyhow::Result<()> {
        if updated {
            let exists =
                Self::check_if_account_event_exists(pool, account_event.account_id).await?;

            if !exists {
                anyhow::bail!(
                    "Account event does not exist for account: {}",
                    account_event.account_id
                );
            }
        }

        let mut transaction = pool.begin().await?;
        let event = serde_json::to_value(&account_event)
            .map_err(|e| anyhow::anyhow!("Failed to serialize account event: {e}"))?;
        let balances = event
            .get("balances")
            .cloned()
            .ok_or_else(|| anyhow::anyhow!("Serialized account event has no balances"))?;
        let margins = event
            .get("margins")
            .cloned()
            .ok_or_else(|| anyhow::anyhow!("Serialized account event has no margins"))?;

        sqlx::query(
            r#"
            INSERT INTO "account" (id) VALUES ($1) ON CONFLICT (id) DO NOTHING
        "#,
        )
        .bind(account_event.account_id.to_string())
        .execute(&mut *transaction)
        .await
        .map(|_| ())

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Read the embedded serde error message to identify the offending field/type in the AccountState.
  2. Sanitize balances/margins before constructing the AccountState: reject or replace NaN/infinite values.
  3. Fix or replace the custom Serialize impl that returns Err.
  4. Align serde_json versions in the dependency tree (cargo tree -i serde_json) if the failure stems from version mismatch.

Example fix

// before: NaN balances blow up during serialization
let state = AccountState { balances: vec![balance_with(nan_price)], .. };

// after: validate before persisting
assert!(state.balances.iter().all(|b| b.total.is_finite()), "non-finite balance");
add_account(&pool, updated, state).await?;
Defensive patterns

Strategy: validation

Validate before calling

// dry-run serialization before calling add_account
serde_json::to_value(&account_event)
    .expect("AccountState must serialize: check for NaN/Inf balances or bad custom Serialize impl");

Type guard

fn is_serializable_account(state: &AccountState) -> bool {
    serde_json::to_value(state).is_ok()
}

Try / catch

let json = serde_json::to_value(&account_event)
    .map_err(|e| { log::error!("account serialization failed: {e}"); e })?;

Prevention

When it happens

Trigger: Calling add_account (or the higher-level cache write_account/adapter) with an AccountState whose serialization to JSON fails — custom Serialize implementations returning Err, or serde_json configured in a mode where non-finite floats hard-error.

Common situations: A custom account event type or Decimal-like type whose Serialize impl can fail; NaN or infinite balance values flowing through a serde_json build that rejects them; mixing serde_json versions across the dependency graph.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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