microsoft/qlib · error · KeyError

The segment is out of valid calendar

Error message

The segment is out of valid calendar

What it means

Raised as KeyError by CalendarProvider.shift when the shifted start index exceeds len(self.cals): after moving forward by step, the segment's start would fall past the last known trading day in the provider's calendar, so no valid dates can be returned.

Source

Thrown at qlib/workflow/task/utils.py:277

        --------
        tuple: new segment

        Raises
        ------
        KeyError:
            shift will raise error if the index(both start and end) is out of self.cal
        """
        if isinstance(seg, tuple):
            start_idx, end_idx = self.align_idx(seg[0], tp_type="start"), self.align_idx(seg[1], tp_type="end")
            if rtype == self.SHIFT_SD:
                start_idx = self._add_step(start_idx, step)
                end_idx = self._add_step(end_idx, step)
            elif rtype == self.SHIFT_EX:
                end_idx = self._add_step(end_idx, step)
            else:
                raise NotImplementedError(f"This type of input is not supported")
            if start_idx is not None and start_idx > len(self.cals):
                raise KeyError("The segment is out of valid calendar")
            return self.get(start_idx), self.get(end_idx)
        else:
            raise NotImplementedError(f"This type of input is not supported")


def replace_task_handler_with_cache(task: dict, cache_dir: Union[str, Path] = ".") -> dict:
    """
    Replace the handler in task with a cache handler.
    It will automatically cache the file and save it in cache_dir.

    >>> import qlib
    >>> qlib.auto_init()
    >>> import datetime
    >>> # it is simplified task
    >>> task = {"dataset": {"kwargs":{'handler': {'class': 'Alpha158', 'module_path': 'qlib.contrib.data.handler', 'kwargs': {'start_time': datetime.date(2008, 1, 1), 'end_time': datetime.date(2020, 8, 1), 'fit_start_time': datetime.date(2008, 1, 1), 'fit_end_time': datetime.date(2014, 12, 31), 'instruments': 'CSI300'}}}}}
    >>> new_task = replace_task_handler_with_cache(task)
    >>> print(new_task)
    {'dataset': {'kwargs': {'handler': 'file...Alpha158.3584f5f8b4.pkl'}}}

View on GitHub (pinned to 79633dd950)

Solutions

  1. Bound the number of rolling folds by (calendar_end_idx - train_start_idx) / step; stop rolling when shift would exceed the calendar.
  2. Update/extend the qlib calendar data (dump_bin / calendars directory) so it covers the full period you roll over.
  3. Catch KeyError and treat it as the natural stop condition of the rolling loop instead of a crash.

Example fix

// before
for i in range(100):
    seg = cal.shift(seg, step=20)  # KeyError on late folds

// after
for i in range(100):
    try:
        seg = cal.shift(seg, step=20)
    except KeyError:
        break  # reached end of calendar
Defensive patterns

Strategy: try-catch

Validate before calling

max_step = len(cal.cals) - cal.align_idx(seg[0], tp_type="start")
if step > max_step:
    raise IndexError(f"shift step {step} exceeds calendar by {step - max_step}")

Try / catch

try:
    next_seg = cal.shift(seg, step)
except KeyError as e:
    if "valid calendar" in str(e):
        break  # natural end of rolling folds
    raise

Prevention

When it happens

Trigger: Calling shift(seg, step) with a step large enough that seg[0] + step runs past the end of the calendar, typically when rolling a train segment forward over many folds until it exceeds the data range.

Common situations: Rolling retrain loops (RollingGen + TaskGen) where the number of folds × step overruns available history; using a short/too-recent calendar (e.g. missing updated calendars day.txt) so valid range is smaller than expected; expanding windows whose end was clamped earlier leaving inconsistent indices.

Related errors


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