nautechsystems/nautilus_trader · critical · anyhow::Error

Binance Futures account state request failed: {e}

Error message

Binance Futures account state request failed: {e}

What it means

refresh_account_state bails when the HTTP query_account call fails, wrapping the underlying transport/API error in a message that duplicates the log::error line above it. This runs during the execution client's connect flow, so the failure prevents the client from finishing connection and no orders can be managed until it succeeds. The meaningful detail is inside the wrapped {e}: the Binance error code for auth, timestamp, or permission problems.

Source

Thrown at crates/adapters/binance/src/futures/execution.rs:469

            account_id,
            account_type,
            balances,
            margins,
            true, // reported
            UUID4::new(),
            ts_now,
            ts_now,
            None, // base currency
        )
        .with_info(info)
    }

    async fn refresh_account_state(&self) -> anyhow::Result<AccountState> {
        let account_info = match self.http_client.query_account().await {
            Ok(info) => info,
            Err(e) => {
                log::error!("Binance Futures account state request failed: {e}");
                anyhow::bail!("Binance Futures account state request failed: {e}");
            }
        };

        Ok(self.create_account_state(&account_info))
    }

    fn update_account_state(&self) {
        let http_client = self.http_client.clone();
        let account_id = self.core.account_id;
        let account_type = self.core.account_type;
        let bnfcr_currency = self.config.bnfcr_currency;
        let emitter = self.emitter.clone();
        let clock = self.clock;

        self.spawn_task("query_account", async move {
            let account_info = http_client
                .query_account()
                .await

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Read the wrapped error code: -2015 invalid key/permissions, -1021 timestamp outside recvWindow, -1003 IP ban/rate limit
  2. Verify the api_key/api_secret pair and that USD-M or COIN-M futures access is enabled for the key
  3. Confirm the environment (Live/Testnet/Demo) matches the credentials and base URL
  4. Sync the system clock via NTP and retry connect with backoff; check network egress to the exchange host
Defensive patterns

Strategy: retry

Validate before calling

// Pre-flight before connect: credentials resolvable and clock in sync
fn preflight_ok() -> bool {
    let key_missing = std::env::var("BINANCE_API_KEY").map_or(true, |v| v.is_empty());
    // optionally GET /fapi/v1/time and compare to local clock; |delta| should be far under recvWindow (default 5s)
    !key_missing
}

Try / catch

// account-state failures surface from the connect flow; retry with backoff, treat
// auth codes (-2015) as fatal, transient network as retryable
const MAX: u32 = 3;
for attempt in 1..=MAX {
    match client.connect().await {
        Ok(()) => break,
        Err(e) if attempt < MAX && !e.to_string().contains("-2015") => {
            tokio::time::sleep(std::time::Duration::from_secs(2u64.pow(attempt))).await;
        }
        Err(e) => return Err(e),
    }
}

Prevention

When it happens

Trigger: Connecting the Binance Futures execution client with invalid or futures-disabled API credentials (-2015), IP-restricted keys used off the allowlist, clock skew beyond recvWindow (-1021), network outage, or a testnet/live environment mismatch between keys and base URL.

Common situations: First live run with keys that lack futures permissions; testnet keys pointed at api.binance.com; NTP drift on the host; firewall blocking egress to the exchange; read-only keys where balances/trading info is restricted.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/ac595e1143d4f4fa. Report an issue: GitHub.