microsoft/qlib · error · ValueError
inner_order_indicators is necessary in un-atomic executor
Error message
inner_order_indicators is necessary in un-atomic executor
What it means
DiskDatasetCache._dataset (qlib/data/cache.py:706) raises ValueError when inst_processors is non-empty and disk_cache != 0. The on-disk dataset cache stores a raw (resampled) frame keyed by the dataset hash, so per-instrument processors (e.g. price normalization) cannot be baked into the shared cache file without corrupting it for other configs. The error message gives the two supported escapes: D.features(disk_cache=0) or qlib.init(dataset_cache=None).
Source
Thrown at qlib/backtest/account.py:383
external trade decision
trade_info : List[(Order, float, float, float)], optional
trading information, by default None
- necessary if atomic is True
- list of tuple(order, trade_val, trade_cost, trade_price)
inner_order_indicators : Indicator, optional
indicators of inner executor, by default None
- necessary if atomic is False
- used to aggregate outer indicators
decision_list: List[Tuple[BaseTradeDecision, pd.Timestamp, pd.Timestamp]] = None,
The decision list of the inner level: List[Tuple[<decision>, <start_time>, <end_time>]]
The inner level
indicator_config : dict, optional
config of calculating indicators, by default {}
"""
if atomic is True and trade_info is None:
raise ValueError("trade_info is necessary in atomic executor")
elif atomic is False and inner_order_indicators is None:
raise ValueError("inner_order_indicators is necessary in un-atomic executor")
# update current position and hold bar count in each bar end
self.update_current_position(trade_start_time, trade_end_time, trade_exchange)
if self.is_port_metr_enabled():
# portfolio_metrics is portfolio related analysis
self.update_portfolio_metrics(trade_start_time, trade_end_time)
self.update_hist_positions(trade_start_time)
# update indicator in each bar end
self.update_indicator(
trade_start_time=trade_start_time,
trade_exchange=trade_exchange,
atomic=atomic,
outer_trade_decision=outer_trade_decision,
trade_info=trade_info,
inner_order_indicators=inner_order_indicators,
decision_list=decision_list,View on GitHub (pinned to 79633dd950)
Solutions
- Call D.features(..., disk_cache=0) — bypasses the dataset cache and applies inst_processors on freshly loaded data
- Disable dataset caching entirely: qlib.init(dataset_cache=None)
- Alternatively drop inst_processors and apply the processing after data retrieval (e.g. in your DataHandlerLP processor chain) so the cache stays valid
Example fix
# before D.features(insts, fields, start, end, inst_processors=[MyProcessor()]) # dataset cache on -> ValueError # after D.features(insts, fields, start, end, disk_cache=0, inst_processors=[MyProcessor()])
Defensive patterns
Strategy: validation
Validate before calling
# decide disk_cache based on processor usage disk_cache = 0 if inst_processors else 1 D.features(insts, fields, start, end, disk_cache=disk_cache, inst_processors=inst_processors)
Type guard
def needs_disk_cache_bypass(inst_processors) -> bool:
return bool(inst_processors) Try / catch
try:
df = D.features(insts, fields, start, end, inst_processors=procs)
except ValueError as e:
if "does not support inst_processor" in str(e):
df = D.features(insts, fields, start, end, disk_cache=0, inst_processors=procs)
else:
raise Prevention
- Never combine inst_processors with disk_cache=1
- Apply instrument-level transforms in DataHandlerLP processors instead of data loading
- Set disk_cache=0 project-wide once inst_processors are in use
When it happens
Trigger: Calling D.features(instruments, fields, ..., inst_processors=[...]) while a dataset cache is configured and disk_cache is left at its default (1); or DatasetCache.dataset(...) reaching _dataset with truthy inst_processors and disk_cache != 0.
Common situations: Using inst_processors like ProcessInstrument (e.g. CSZScoreNorm-style per-instrument transforms at data-loading time) together with dataset caching enabled; the cache would silently return unprocessed data if this guard were absent — hence the hard error. Related FIXME: resampled cache read back with end_time truncation can also yield incomplete dates.
Related errors
- generate_portfolio_metrics should be True if you want to gen
- This type of input is not supported
- unknown loss `%s`
- unknown metric `%s`
- Empty data from dataset, please check your dataset config.
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/0543d1080f69d237.
Report an issue: GitHub.