nautechsystems/nautilus_trader · error · anyhow::Error

invalid RTDS crypto TWAP payload: {e}

Error message

invalid RTDS crypto TWAP payload: {e}

What it means

When a crypto TWAP message arrives on a subscribed RTDS topic, the envelope payload string is deserialized into CryptoTwapPayloadRaw. Failure raises 'invalid RTDS crypto TWAP payload: {e}'. This guards against malformed or schema-drifted payloads before window validation.

Source

Thrown at crates/adapters/polymarket/src/rtds.rs:1303

                ts_init,
            ));

            self.emit_custom_payload(&custom_payload, data_types.clone());
        }
    }

    fn handle_crypto_twap_update(
        &self,
        envelope: &RtdsEnvelope,
        window: RtdsCryptoTwapWindow,
    ) -> anyhow::Result<()> {
        let topic = window.topic();
        if !self.has_topic_subscription(topic.as_str()) {
            return Ok(());
        }

        let payload: CryptoTwapPayloadRaw = serde_json::from_str(envelope.payload.get())
            .map_err(|e| anyhow::anyhow!("invalid RTDS crypto TWAP payload: {e}"))?;
        if payload.window_s != window.seconds() {
            anyhow::bail!(
                "RTDS TWAP topic {:?} requires window_s={}, received {}",
                topic.as_str(),
                window.seconds(),
                payload.window_s,
            );
        }
        let symbol_lower = payload.symbol.to_ascii_lowercase();
        let value =
            decimal_from_signed_e18("full_accuracy_value", payload.full_accuracy_value.as_str())?;
        let ts_event = unix_nanos_from_millis("payload.timestamp", payload.timestamp)?;
        unix_nanos_from_millis("envelope.timestamp", envelope.timestamp)?;
        let Some(data_types) =
            self.admit_twap_observation(topic, &symbol_lower, payload.timestamp, value)?
        else {
            return Ok(());
        };

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log the raw envelope payload alongside the serde error to see what the gateway actually sent
  2. Verify the subscription topic/window matches the crypto TWAP schema you expect
  3. Update the adapter if Polymarket changed the CryptoTwapPayloadRaw field names or types
  4. Add a tolerance/skip path: ignore unparseable messages instead of failing the message loop if they are non-essential

Example fix

// before
let payload: CryptoTwapPayloadRaw = serde_json::from_str(envelope.payload.get())?;
// after
let payload: CryptoTwapPayloadRaw = serde_json::from_str(envelope.payload.get())
    .inspect_err(|e| warn!("TWAP payload: {} err: {e}", envelope.payload.get()))?;
Defensive patterns

Strategy: try-catch

Try / catch

// Rust: log and skip malformed TWAP messages instead of failing the loop
let payload: CryptoTwapPayloadRaw = match serde_json::from_str(envelope.payload.get()) {
    Ok(p) => p,
    Err(e) => { warn!("skipping invalid TWAP payload: {e}"); return Ok(()); }
};

Prevention

When it happens

Trigger: Receiving a message on a subscribed crypto TWAP topic whose JSON does not match CryptoTwapPayloadRaw (missing fields like window_s, wrong types, HTML/JSON error bodies, empty payload).

Common situations: Polymarket RTDS schema changes to the TWAP payload, gateway emitting error messages on subscribed topics, or proxy/firewall injecting non-JSON content into the stream.

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/2a958fb1a5126c5c. Report an issue: GitHub.