nautechsystems/nautilus_trader · error · ValueError

BacktestNode state is unavailable when dispose_on_completion

Error message

BacktestNode state is unavailable when dispose_on_completion=True; set dispose_on_completion=False before running the backtest

What it means

BetfairStreamClient::connect failed while establishing the push stream websocket used for market data; the underlying transport or authentication error is re-wrapped as a string. Typical causes are websocket egress blocked while REST is allowed, wrong stream_host/stream_port overrides in BetfairStreamConfig, or the stream endpoint rejecting the session token/app key.

Source

Thrown at python/nautilus_trader/analysis/tearsheet.py:541

        stats_pnls=_filter_stats_pnls(result.stats_pnls, currency),
        stats_returns=result.stats_returns,
        stats_general=result.stats_general,
        returns=returns,
        output_path=output_path,
        title=title,
        config=config,
        benchmark_returns=benchmark_returns,
        benchmark_name=benchmark_name,
        engine=engine_view,
    )


def _validate_result_node_state(node: BacktestNode, run_config_id: str) -> None:
    for run_config in node.configs:
        if run_config.id != run_config_id:
            continue
        if run_config.dispose_on_completion:
            raise ValueError(
                "BacktestNode state is unavailable when dispose_on_completion=True; "
                "set dispose_on_completion=False before running the backtest",
            )
        return


def _filter_stats_pnls(stats_pnls, currency) -> dict:
    stats_pnls = dict(stats_pnls)
    if currency is None:
        return stats_pnls

    currency_code = getattr(currency, "code", str(currency))
    return {currency_code: stats_pnls[currency_code]} if currency_code in stats_pnls else {}


def _result_returns_series(result: BacktestResult) -> pd.Series:
    returns = pd.Series(dict(result.returns_series), dtype="float64")
    returns.index = pd.to_datetime(returns.index, unit="ns", utc=True)

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Verify websocket egress to the Betfair stream host from the same environment
  2. Remove custom stream_host/stream_port overrides so defaults apply
  3. Confirm the app key has streaming access and the session token is fresh
  4. Retry after Betfair-side incidents clear

Example fix

// before: single attempt
let client = BetfairStreamClient::connect(&cred, token, handler, cfg).await?;

// after: bounded retry with backoff
let client = retry_with_backoff(5, |token| async {
    BetfairStreamClient::connect(&cred, token.clone(), handler.clone(), cfg.clone()).await
}, token).await?;
Defensive patterns

Strategy: retry

Validate before calling

// preflight: confirm the stream endpoint is reachable before starting
let host = stream_config.host.clone();
anyhow::ensure!(
    tokio::net::TcpStream::connect((host.as_str(), stream_config.port)).await.is_ok(),
    "no egress to Betfair stream host {host}:{}",
    stream_config.port
);

Try / catch

match BetfairStreamClient::connect(&cred, token, handler, cfg).await {
    Ok(client) => { /* proceed */ }
    Err(e) => {
        // check for auth/entitlement in the message; retry transient transport
        // failures with backoff, escalate persistent ones
    }
}

Prevention

When it happens

Trigger: Starting the data client where firewalls or proxies permit HTTPS REST but block the Betfair stream websocket; custom stream_host/stream_port set incorrectly in config; app key without streaming entitlements; Betfair stream endpoint maintenance.

Common situations: Corporate networks allowing 443 REST but terminating websockets; config copied from a test environment with stale host overrides; delayed app keys that cannot stream; Betfair stream incidents.

Related errors


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