nautechsystems/nautilus_trader · error

RTDS TWAP topic {:?} requires window_s={}, received {}

Error message

RTDS TWAP topic {:?} requires window_s={}, received {}

What it means

Raised when a crypto TWAP message arrives whose payload window_s does not match the window implied by its topic (30 for crypto_prices_twap_thirty, 60 for crypto_prices_twap_sixty). The topic and payload disagree, so trusting the payload would corrupt the price series; the feed validates and bails. This guards against upstream protocol changes or misrouted messages.

Source

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

            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(());
        };

        let ts_init = self.inner.clock.get_time_ns();

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Upgrade the adapter in case upstream changed topic/window semantics and a fix exists.
  2. Capture the full envelope (topic + payload) and report the mismatch to the venue/adapter maintainers.
  3. If replaying recorded data, regenerate fixtures so payload.window_s matches the topic's required window.
  4. As a stopgap, drop the subscription to the offending topic if the data is not required.

Example fix

// before
// fixture: topic "crypto_prices_twap_thirty" with window_s=60
{"topic":"crypto_prices_twap_thirty","payload":"{\"window_s\":60,...}"}
// after
{"topic":"crypto_prices_twap_thirty","payload":"{\"window_s\":30,...}"}
Defensive patterns

Strategy: validation

Validate before calling

fn twap_window_matches(topic: &str, window_s: u64) -> bool {
    matches!((topic, window_s), ("crypto_prices_twap_thirty", 30) | ("crypto_prices_twap_sixty", 60))
}

Type guard

fn parse_twap_payload(topic: &str, raw: &str) -> anyhow::Result<CryptoTwapPayloadRaw> {
    let p: CryptoTwapPayloadRaw = serde_json::from_str(raw)?;
    let required = if topic.ends_with("thirty") { 30 } else { 60 };
    anyhow::ensure!(p.window_s == required, "window_s {} != required {required}", p.window_s);
    Ok(p)
}

Try / catch

match feed.next_event().await {
    Err(e) if e.to_string().contains("RTDS TWAP topic") => {
        log::error!("TWAP topic/payload mismatch (upstream protocol change?): {e}");
    }
    other => { /* normal handling */ }
}

Prevention

When it happens

Trigger: RTDS delivers a TWAP message on crypto_prices_twap_thirty with payload.window_s != 30 (or the sixty variant with window_s != 60) — e.g. Polymarket changes window units or routes messages to the wrong topic.

Common situations: Upstream API changing TWAP payload semantics; misrouted messages from the publisher; replaying recorded fixtures with mismatched topic/payload pairs.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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