nautechsystems/nautilus_trader · error · ValueError
BacktestNode state is unavailable when…
Error message
BacktestNode state is unavailable when dispose_on_completion=True; set dispose_on_completion=False before running the backtest
What it means
The tearsheet reads chart data from the BacktestNode's internal engine state after a run. If the run config had dispose_on_completion=True, the engine is torn down and its state discarded once the backtest finishes, so the library refuses to proceed rather than fail later with missing data.
Solutions
- Set dispose_on_completion=False on the BacktestRunConfig before running the backtest, then re-run.
- If the run already completed with disposal, re-execute the backtest with disposal disabled before generating the tearsheet.
- Generate tearsheets without node-dependent charts (remove 'bars_with_fills' from chart_names) and pass node=None.
Example fix
// before config = BacktestRunConfig(...) node = BacktestNode(configs=[config]) node.run() // after config = BacktestRunConfig(..., dispose_on_completion=False) node = BacktestNode(configs=[config]) node.run()
Defensive patterns
Strategy: validation
Validate before calling
if node is not None:
for rc in node.configs:
if rc.dispose_on_completion:
raise ValueError(f'Run config {rc.id} has dispose_on_completion=True; tearsheet needs engine state') Try / catch
try:
path = create_tearsheet(result, config=config, node=node, run_config_id=rid)
except ValueError as e:
if 'dispose_on_completion' in str(e):
config.dispose_on_completion = False
node.run() # re-run with state retained
path = create_tearsheet(result, config=config, node=node, run_config_id=rid)
else:
raise Prevention
- Always set dispose_on_completion=False on run configs that will be visualized.
- Build node-based tearsheets immediately after node.run() in the same process.
- If running many configs in parallel, keep disposal enabled for non-visualized runs and re-run only the ones needing charts.
When it happens
Trigger: Running a backtest via BacktestNode whose BacktestRunConfig has dispose_on_completion=True (the teardown/default in some wrappers), then calling create_tearsheet with that node and the matching run_config_id for charts that need engine data (e.g. bars_with_fills).
Common situations: Reusing a node from a distributed/parallel run where dispose_on_completion defaults to True; configuring the node for memory efficiency and later adding visualization; following older examples written before tearsheets consumed node state.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- account report contains multiple currencies
- run_config_id is required when a BacktestNode is provided
- A different extractor is already registered for
- active Lighter binding conflicts at client_order_index
- Actor is already registered
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/30cf4a2a6230a3e4.
Report an issue: GitHub.
Appendix: source
Thrown at python/nautilus_trader/analysis/tearsheet.py:555
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: Mapping[str, Any],
currency: Currency | None,
) -> dict[str, Any]:
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 {}
View on GitHub (pinned to 18893faf8b)