nautechsystems/nautilus_trader · error · anyhow::Error

CustomData value must be valid JSON: {e}

Error message

CustomData value must be valid JSON: {e}

What it means

Raised in `add_custom_data` when the freshly serialized CustomData bytes fail to parse back via `serde_json::from_slice`. This is a defensive round-trip check before extracting the `data_type` field; failure indicates the serialized bytes are not valid JSON (rare, usually a bug or non-UTF8/binary payload) .

Source

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

        .bind(name)
        .fetch_all(pool)
        .await
        .map(|rows| rows.into_iter().map(|row| row.0).collect())
        .map_err(|e| anyhow::anyhow!("Failed to load signals: {e}"))
    }

    /// Inserts a `CustomData` entry via the provided `pool`.
    ///
    /// Serializes the model `CustomData` to full JSON and stores it in the JSONB `value` column.
    ///
    /// # Errors
    ///
    /// Returns an error if the SQL INSERT operation fails.
    pub async fn add_custom_data(pool: &PgPool, data: &CustomData) -> anyhow::Result<()> {
        let json_bytes = serde_json::to_vec(data)
            .map_err(|e| anyhow::anyhow!("CustomData must be valid JSON: {e}"))?;
        let value_json: serde_json::Value = serde_json::from_slice(&json_bytes)
            .map_err(|e| anyhow::anyhow!("CustomData value must be valid JSON: {e}"))?;
        let data_type_obj = value_json
            .get("data_type")
            .and_then(|v| v.as_object())
            .ok_or_else(|| anyhow::anyhow!("CustomData JSON missing data_type"))?;
        let data_type_name = data_type_obj
            .get("type_name")
            .and_then(|v| v.as_str())
            .unwrap_or("");
        let metadata_json = data_type_obj
            .get("metadata")
            .cloned()
            .unwrap_or_else(|| serde_json::Value::Object(serde_json::Map::new()));
        let identifier = data_type_obj
            .get("identifier")
            .and_then(|v| v.as_str())
            .unwrap_or("");
        sqlx::query(
            r#"

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Confirm the payload serializes and deserializes cleanly with serde_json::to_vec/from_slice in a unit test.
  2. Inspect the serde_json error in `{e}` for the offset of the invalid byte.
  3. Avoid custom Serialize impls; derive Serialize/Deserialize on the payload type.
  4. Update to matching versions of the data model crate so serialization output is well-formed.
Defensive patterns

Strategy: validation

Validate before calling

let bytes = serde_json::to_vec(&data)?;
serde_json::from_slice::<serde_json::Value>(&bytes)
    .map_err(|e| anyhow::anyhow!("payload does not round-trip: {e}"))?;

Type guard

fn round_trips<T: serde::Serialize>(v: &T) -> bool {
    serde_json::to_vec(v)
        .ok()
        .map(|b| serde_json::from_slice::<serde_json::Value>(&b).is_ok())
        .unwrap_or(false)
}

Prevention

When it happens

Trigger: Calling `add_custom_data` when serialization produced bytes that serde_json cannot parse back — e.g., a custom Serialize impl emitting invalid JSON, or corrupted intermediate representation.

Common situations: Hand-written Serialize implementations producing malformed output; exotic custom data payloads; library version mismatch where CustomData's serialized shape changed.

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/0224e0406c5e64f4. Report an issue: GitHub.