{"record":{"id":"e6fc9dedc8e0cc99","repo":"google-research/timesfm","slug":"horizon-must-be-less-than-the-max-horizon-horizo-e6fc9d","errorCode":null,"errorMessage":"Horizon must be less than the max horizon. {horizon} > {fc.max_horizon}.","messagePattern":"Horizon must be less than the max horizon\\. (.+?) > (.+?)\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/timesfm/timesfm_2p5/timesfm_2p5_torch.py","lineNumber":423,"sourceCode":"        self.model.o,\n        new_horizon := math.ceil(fc.max_horizon / self.model.o) * self.model.o,\n      )\n      fc = dataclasses.replace(fc, max_horizon=new_horizon)\n    if fc.max_context + fc.max_horizon > self.model.config.context_limit:\n      raise ValueError(\n        \"Context + horizon must be less than the context limit.\"\n        f\" {fc.max_context} + {fc.max_horizon} >\"\n        f\" {self.model.config.context_limit}.\"\n      )\n    if fc.use_continuous_quantile_head and (fc.max_horizon > self.model.os):\n      raise ValueError(\n        f\"Continuous quantile head is not supported for horizons > {self.model.os}.\"\n      )\n    self.forecast_config = fc\n\n    def _compiled_decode(horizon, inputs, masks):\n      if horizon > fc.max_horizon:\n        raise ValueError(\n          f\"Horizon must be less than the max horizon. {horizon} > {fc.max_horizon}.\"\n        )\n\n      inputs = (\n        torch.from_numpy(np.array(inputs)).to(self.model.device).to(torch.float32)\n      )\n      masks = torch.from_numpy(np.array(masks)).to(self.model.device).to(torch.bool)\n      batch_size = inputs.shape[0]\n\n      if fc.infer_is_positive:\n        is_positive = torch.all(inputs >= 0, dim=-1, keepdim=True)\n      else:\n        is_positive = None\n\n      if fc.normalize_inputs:\n        mu = torch.mean(inputs, dim=-1, keepdim=True)\n        sigma = torch.std(inputs, dim=-1, keepdim=True)\n        inputs = revin(inputs, mu, sigma, reverse=False)","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/google-research/timesfm/blob/331c6d33cb1ac2611de3056d0ac7164aab6301eb/src/timesfm/timesfm_2p5/timesfm_2p5_torch.py#L405-L441","documentation":"_compiled_decode is the compiled inference closure created inside compile(); torch.compile fixes graph shapes sized by forecast_config.max_horizon, so requesting a horizon larger than the compiled maximum raises ValueError at inference time. Every forecast() call on a compiled model passes through this check.","triggerScenarios":"Calling model.forecast(horizon=N) (or forecast_on_df) with N greater than the max_horizon supplied to the preceding compile() — e.g. compile with max_horizon=128 then forecast(horizon=256).","commonSituations":"Choosing the horizon per-request after compiling with a small max_horizon; reusing a compiled model configured for short horizons in a new long-horizon use case; confusion because max_horizon was silently rounded up to a multiple of 128 at compile time.","solutions":["Recompile with a larger max_horizon (multiple of output patch length 128) covering every horizon you request.","Clamp the per-call horizon to model.forecast_config.max_horizon before calling forecast.","Compile once with the largest needed horizon and slice outputs down for smaller requests.","Keep compile-time max_horizon and inference horizons in the same app config so they stay in sync."],"exampleFix":"// before\nmodel.compile(ForecastConfig(max_horizon=128))\nmodel.forecast(horizon=256, inputs=...)  # ValueError: 256 > 128\n// after\nmodel.compile(ForecastConfig(max_horizon=256))\nmodel.forecast(horizon=256, inputs=...)  # OK","handlingStrategy":"validation","validationCode":"max_h = model.forecast_config.max_horizon\nhorizon = min(horizon, max_h)\npredictions = model.forecast(horizon=horizon, inputs=inputs)","typeGuard":null,"tryCatchPattern":"try:\n    preds = model.forecast(horizon=h, inputs=inputs)\nexcept ValueError as e:\n    if \"max horizon\" in str(e):\n        h = model.forecast_config.max_horizon\n        preds = model.forecast(horizon=h, inputs=inputs)\n    else:\n        raise","preventionTips":["Compile with the largest horizon you will ever request.","Clamp request horizons to model.forecast_config.max_horizon.","Slice compiled outputs for smaller horizons instead of recompiling.","Keep compile-time max_horizon and inference horizons in shared config."],"tags":["configuration","value-error","inference","forecasting"],"backgroundTag":"horizon-exceeds-compiled-max","analyzedSha":"331c6d33cb1ac2611de3056d0ac7164aab6301eb","analyzedAt":"2026-08-29T01:04:23.138Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}