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
- Log the raw envelope payload alongside the serde error to see what the gateway actually sent
- Verify the subscription topic/window matches the crypto TWAP schema you expect
- Update the adapter if Polymarket changed the CryptoTwapPayloadRaw field names or types
- 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
- Log the raw payload on every deserialization failure to catch schema drift early
- Pin adapter versions to compatible RTDS message schemas and update on API changes
- Ignore non-data frames (pings/errors) routed onto subscribed topics
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
- failed to send RTDS {action} request: {e}
- invalid RTDS JSON frame
- fee exponent must be a decimal number or numeric string
- Expected 2 token IDs, received {}
- Expected 2 outcomes, received {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/2a958fb1a5126c5c.
Report an issue: GitHub.