{"record":{"id":"63bf1ef5b87e10a7","repo":"nautechsystems/nautilus_trader","slug":"invalid-rtds-json-frame","errorCode":null,"errorMessage":"invalid RTDS JSON frame","messagePattern":"invalid RTDS JSON frame","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/polymarket/src/rtds.rs","lineNumber":1151,"sourceCode":"                    if self.clear_ws_if_current(&ws) {\n                        self.request_reconcile(ReconcileReason::TransportReset);\n                    }\n                    break;\n                }\n            }\n        }\n    }\n\n    fn handle_text_message(&self, text: &str) -> anyhow::Result<()> {\n        if text.trim().is_empty() {\n            return Ok(());\n        }\n\n        let envelope: RtdsEnvelope = match serde_json::from_str(text) {\n            Ok(envelope) => envelope,\n            Err(e) => {\n                if self.malformed_frame_requires_visible_twap_failure(text) {\n                    return Err(anyhow::Error::new(e).context(\"invalid RTDS JSON frame\"));\n                }\n                log::debug!(\"Ignoring non-RTDS JSON frame: {e}\");\n                return Ok(());\n            }\n        };\n\n        self.handle_envelope(&envelope)\n    }\n\n    fn handle_envelope(&self, envelope: &RtdsEnvelope) -> anyhow::Result<()> {\n        match (envelope.topic.as_str(), envelope.msg_type.as_str()) {\n            (\"crypto_prices\", \"subscribe\") => {\n                self.handle_crypto_price_subscribe(envelope);\n            }\n            (\"crypto_prices\", \"update\") => {\n                self.handle_crypto_price_update(envelope);\n            }\n            (\"crypto_prices_twap_thirty\", \"update\") => {","sourceCodeStart":1133,"sourceCodeEnd":1169,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/polymarket/src/rtds.rs#L1133-L1169","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["Log the offending raw text and compare it to the RtdsEnvelope struct to find the schema drift.","Update the nautilus OKX/Polymarket adapter (and its RTDS types) to the version matching the current Polymarket protocol.","If a new event variant is the cause, extend RtdsEnvelope (e.g. #[serde(tag)] untagged variants) to accept it.","Pin the Polymarket service version if a server-side rollout broke compatibility.","Check for intermediaries (proxies) corrupting the frame payload."],"exampleFix":"// before\n#[derive(Deserialize)]\nstruct RtdsEnvelope { event_type: String, payload: serde_json::Value }\n// after\n#[derive(Deserialize)]\n#[serde(untagged)]\nenum RtdsEnvelope { Known { event_type: String, payload: serde_json::Value }, Unknown(serde_json::Value) }","handlingStrategy":"type-guard","validationCode":"// peek at frame before full parse\nlet v: serde_json::Value = serde_json::from_str(text)?;\nif v.get(\"type\").is_none() { return Ok(()); } // not an RTDS frame","typeGuard":"fn is_rtds_envelope(v: &serde_json::Value) -> bool {\n    v.get(\"type\").and_then(|t| t.as_str()).is_some()\n        && v.get(\"payload\").map_or(false, |p| p.is_object())\n}","tryCatchPattern":"match handler.handle_frame(text) {\n    Ok(()) => {}\n    Err(e) if e.to_string().contains(\"invalid RTDS JSON frame\") => {\n        error!(\"RTDS schema drift: {e}; raw={text}\");\n        alert_and_reconnect();\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Keep the adapter updated with Polymarket RTDS protocol changes","Log raw frames that fail parsing to detect schema drift early","Use serde untagged/lenient variants for unknown event types","Add a CI test deserializing recorded live RTDS samples"],"tags":["polymarket","rtds","websocket","json","schema"],"backgroundTag":"invalid-json-response","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}