nautechsystems/nautilus_trader · error · anyhow::Error

invalid RTDS JSON frame

Error message

invalid RTDS JSON frame

What it means

The Polymarket RTDS (real-time data socket) handler parses each incoming text frame as an RtdsEnvelope. If JSON deserialization fails AND the frame looks like a genuine RTDS message (per malformed_frame_requires_visible_twap_failure), the error is surfaced as "invalid RTDS JSON frame"; frames that don't even resemble RTDS payloads are silently ignored at debug level. This error means a real RTDS frame arrived but its JSON did not match the expected envelope schema.

Source

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

                    if self.clear_ws_if_current(&ws) {
                        self.request_reconcile(ReconcileReason::TransportReset);
                    }
                    break;
                }
            }
        }
    }

    fn handle_text_message(&self, text: &str) -> anyhow::Result<()> {
        if text.trim().is_empty() {
            return Ok(());
        }

        let envelope: RtdsEnvelope = match serde_json::from_str(text) {
            Ok(envelope) => envelope,
            Err(e) => {
                if self.malformed_frame_requires_visible_twap_failure(text) {
                    return Err(anyhow::Error::new(e).context("invalid RTDS JSON frame"));
                }
                log::debug!("Ignoring non-RTDS JSON frame: {e}");
                return Ok(());
            }
        };

        self.handle_envelope(&envelope)
    }

    fn handle_envelope(&self, envelope: &RtdsEnvelope) -> anyhow::Result<()> {
        match (envelope.topic.as_str(), envelope.msg_type.as_str()) {
            ("crypto_prices", "subscribe") => {
                self.handle_crypto_price_subscribe(envelope);
            }
            ("crypto_prices", "update") => {
                self.handle_crypto_price_update(envelope);
            }
            ("crypto_prices_twap_thirty", "update") => {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log the offending raw text and compare it to the RtdsEnvelope struct to find the schema drift.
  2. Update the nautilus OKX/Polymarket adapter (and its RTDS types) to the version matching the current Polymarket protocol.
  3. If a new event variant is the cause, extend RtdsEnvelope (e.g. #[serde(tag)] untagged variants) to accept it.
  4. Pin the Polymarket service version if a server-side rollout broke compatibility.
  5. Check for intermediaries (proxies) corrupting the frame payload.

Example fix

// before
#[derive(Deserialize)]
struct RtdsEnvelope { event_type: String, payload: serde_json::Value }
// after
#[derive(Deserialize)]
#[serde(untagged)]
enum RtdsEnvelope { Known { event_type: String, payload: serde_json::Value }, Unknown(serde_json::Value) }
Defensive patterns

Strategy: type-guard

Validate before calling

// peek at frame before full parse
let v: serde_json::Value = serde_json::from_str(text)?;
if v.get("type").is_none() { return Ok(()); } // not an RTDS frame

Type guard

fn is_rtds_envelope(v: &serde_json::Value) -> bool {
    v.get("type").and_then(|t| t.as_str()).is_some()
        && v.get("payload").map_or(false, |p| p.is_object())
}

Try / catch

match handler.handle_frame(text) {
    Ok(()) => {}
    Err(e) if e.to_string().contains("invalid RTDS JSON frame") => {
        error!("RTDS schema drift: {e}; raw={text}");
        alert_and_reconnect();
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Receiving an RTDS WebSocket message whose JSON shape does not match RtdsEnvelope (missing/renamed fields, unexpected event type, non-string fields where strings are expected), and the frame heuristically requires a visible failure (e.g. it affects TWAP handling).

Common situations: Polymarket changing/adding RTDS event schemas after an adapter upgrade; subscribing to a channel producing a message variant the adapter doesn't model; a proxy or gateway mangling/re-truncating frames; version mismatch between adapter and current RTDS protocol.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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