nautechsystems/nautilus_trader · error

{e}

Error message

{e}

What it means

load_instruments fetches Betfair's navigation data via send_navigation (the listNavigationData REST endpoint) and this wraps any failure of that call as {e}. The wrapped BetfairHttpError is typically a missing/invalid session token, an application key mismatched to the endpoint environment, a network failure, or a decode error on the navigation JSON.

Source

Thrown at crates/adapters/betfair/src/provider.rs:282

/// 1. Fetches the navigation tree via `send_navigation`
/// 2. Flattens and filters to matching market IDs
/// 3. Batches market IDs (max 50 per request)
/// 4. Calls `listMarketCatalogue` for each batch
/// 5. Parses results into [`InstrumentAny`] via `parse_market_catalogue`
///
/// # Errors
///
/// Returns an error if any API request fails or instrument parsing fails.
pub async fn load_instruments(
    client: &BetfairHttpClient,
    filter: &NavigationFilter,
    currency: Currency,
    min_notional: Option<Money>,
) -> anyhow::Result<Vec<InstrumentAny>> {
    let navigation: Navigation = client
        .send_navigation()
        .await
        .map_err(|e| anyhow::anyhow!("{e}"))?;

    let all_markets = flatten_navigation(&navigation);

    let filtered: Vec<&FlattenedMarket> =
        all_markets.iter().filter(|m| filter.matches(m)).collect();

    log::debug!("Found {} markets matching filter", filtered.len());

    let market_ids: Vec<MarketId> = filtered
        .iter()
        .filter_map(|m| m.market_id.clone())
        .collect::<AHashSet<_>>()
        .into_iter()
        .collect();

    let time_range =
        if filter.min_market_start_time.is_some() || filter.max_market_start_time.is_some() {
            Some(TimeRange {

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Confirm login succeeds and a session token exists before loading instruments.
  2. Verify the application key matches the environment of the configured endpoints.
  3. Check network/proxy reachability of the navigation URL.
  4. Retry the load — transient failures clear on the next attempt.
Defensive patterns

Strategy: retry

Validate before calling

// verify session readiness before heavy instrument loads
if !http_client.is_connected().await {
    http_client.connect().await?;
}

Try / catch

match load_instruments(&client, &filter, currency, min_notional).await {
    Ok(instruments) => { /* store */ }
    Err(e) => {
        log::warn!("navigation load failed, retrying: {e}");
        tokio::time::sleep(Duration::from_secs(5)).await;
        // retry once; escalate on repeated failure
    }
}

Prevention

When it happens

Trigger: Running the instrument provider's load path (load_instruments) before login completed, with an app key lacking the right product scope, or when the navigation endpoint is unreachable from the host.

Common situations: Delayed vs live app key mismatch; provider warm-up with expired credentials; proxy/firewall blocking the navigation URL.

Related errors


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