{"record":{"id":"0fb835486974cc2a","repo":"microsoft/qlib","slug":"the-segment-is-out-of-valid-calendar","errorCode":null,"errorMessage":"The segment is out of valid calendar","messagePattern":"The segment is out of valid calendar","errorType":"exception","errorClass":"KeyError","httpStatus":null,"severity":"error","filePath":"qlib/workflow/task/utils.py","lineNumber":277,"sourceCode":"        --------\n        tuple: new segment\n\n        Raises\n        ------\n        KeyError:\n            shift will raise error if the index(both start and end) is out of self.cal\n        \"\"\"\n        if isinstance(seg, tuple):\n            start_idx, end_idx = self.align_idx(seg[0], tp_type=\"start\"), self.align_idx(seg[1], tp_type=\"end\")\n            if rtype == self.SHIFT_SD:\n                start_idx = self._add_step(start_idx, step)\n                end_idx = self._add_step(end_idx, step)\n            elif rtype == self.SHIFT_EX:\n                end_idx = self._add_step(end_idx, step)\n            else:\n                raise NotImplementedError(f\"This type of input is not supported\")\n            if start_idx is not None and start_idx > len(self.cals):\n                raise KeyError(\"The segment is out of valid calendar\")\n            return self.get(start_idx), self.get(end_idx)\n        else:\n            raise NotImplementedError(f\"This type of input is not supported\")\n\n\ndef replace_task_handler_with_cache(task: dict, cache_dir: Union[str, Path] = \".\") -> dict:\n    \"\"\"\n    Replace the handler in task with a cache handler.\n    It will automatically cache the file and save it in cache_dir.\n\n    >>> import qlib\n    >>> qlib.auto_init()\n    >>> import datetime\n    >>> # it is simplified task\n    >>> 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'}}}}}\n    >>> new_task = replace_task_handler_with_cache(task)\n    >>> print(new_task)\n    {'dataset': {'kwargs': {'handler': 'file...Alpha158.3584f5f8b4.pkl'}}}","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/microsoft/qlib/blob/79633dd9506ea689e5400dea0197717b5b3d74b7/qlib/workflow/task/utils.py#L259-L295","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Bound the number of rolling folds by (calendar_end_idx - train_start_idx) / step; stop rolling when shift would exceed the calendar.","Update/extend the qlib calendar data (dump_bin / calendars directory) so it covers the full period you roll over.","Catch KeyError and treat it as the natural stop condition of the rolling loop instead of a crash."],"exampleFix":"// before\nfor i in range(100):\n    seg = cal.shift(seg, step=20)  # KeyError on late folds\n\n// after\nfor i in range(100):\n    try:\n        seg = cal.shift(seg, step=20)\n    except KeyError:\n        break  # reached end of calendar","handlingStrategy":"try-catch","validationCode":"max_step = len(cal.cals) - cal.align_idx(seg[0], tp_type=\"start\")\nif step > max_step:\n    raise IndexError(f\"shift step {step} exceeds calendar by {step - max_step}\")","typeGuard":null,"tryCatchPattern":"try:\n    next_seg = cal.shift(seg, step)\nexcept KeyError as e:\n    if \"valid calendar\" in str(e):\n        break  # natural end of rolling folds\n    raise","preventionTips":["Compute the maximum feasible folds from calendar length before the rolling loop.","Keep qlib calendar data up to date so the valid range matches your backtest horizon.","Treat KeyError from shift as a loop terminator, not a crash."],"tags":["qlib","calendar","shift","out-of-range","rolling"],"backgroundTag":null,"analyzedSha":"79633dd9506ea689e5400dea0197717b5b3d74b7","analyzedAt":"2026-08-15T07:01:27.511Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}