microsoft/qlib · error · ValueError

cannot assign data as `num_states==0`

Error message

cannot assign data as `num_states==0`

What it means

assign_data on the RL dataset writes into the internal state-memory tensor, which only exists when the dataset was created with num_states > 0. With num_states == 0 the memory array has zero width and cannot store anything, so qlib raises ValueError instead of silently dropping data or corrupting shapes.

Source

Thrown at qlib/contrib/data/dataset.py:244

        obj._memory = self._memory
        obj._zeros = self._zeros
        # update index for this batch
        date_index = self._index.get_level_values(1)
        obj._batch_slices = self._batch_slices[(date_index >= start_date) & (date_index <= end_date)]
        mask = (self._daily_index.values >= start_date) & (self._daily_index.values <= end_date)
        obj._daily_slices = self._daily_slices[mask]
        obj._daily_index = self._daily_index[mask]
        return obj

    def restore_index(self, index):
        return self._index[index]

    def restore_daily_index(self, daily_index):
        return pd.Index(self._daily_index.loc[daily_index])

    def assign_data(self, index, vals):
        if self.num_states == 0:
            raise ValueError("cannot assign data as `num_states==0`")
        if isinstance(vals, torch.Tensor):
            vals = vals.detach().cpu().numpy()
        self._memory[index] = vals

    def clear_memory(self):
        if self.num_states == 0:
            raise ValueError("cannot clear memory as `num_states==0`")
        self._memory[:] = 0

    def train(self):
        """enable traning mode"""
        self.batch_size, self.n_samples, self.drop_last, self.shuffle = self.params

    def eval(self):
        """enable evaluation mode"""
        self.batch_size = -1
        self.n_samples = None
        self.drop_last = False

View on GitHub (pinned to 79633dd950)

Solutions

  1. Construct the dataset with num_states > 0 equal to your state dimension if you intend to call assign_data.
  2. Or guard the training loop: skip assign_data/clear_memory when dataset.num_states == 0.
  3. Check that the num_states value survives your config merge — a missing key defaulting to 0 is the usual root cause.

Example fix

# before
ds = RLDataSet(data, seq_len=20, num_states=0)
ds.assign_data(idx, states)  # -> ValueError
# after
ds = RLDataSet(data, seq_len=20, num_states=states.shape[1])
ds.assign_data(idx, states)
Defensive patterns

Strategy: validation

Validate before calling

if ds.num_states == 0:
    raise ValueError('dataset built with num_states=0 cannot store state data; rebuild with num_states>0')
ds.assign_data(index, vals)

Type guard

def can_assign_state(ds) -> bool:
    return getattr(ds, 'num_states', 0) > 0

Try / catch

try:
    ds.assign_data(idx, vals)
except ValueError as e:
    if 'num_states==0' in str(e):
        pass  # memoryless dataset: nothing to store
    else:
        raise

Prevention

When it happens

Trigger: Building the dataset with num_states=0 (pure supervised mode) but then calling assign_data(index, vals) — typically from an RL training loop that stores state/action data each batch.

Common situations: Reusing a generic RL training script against a dataset configured for supervised learning; refactoring where num_states is read from config and defaults to 0 while the training loop still calls assign_data.

Related errors


AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15). Data as JSON: /api/errors/91505d8ecf0e15d3. Report an issue: GitHub.