You feed a time-series foundation model a year of hourly data and get back a forecast that looks plausible. Then you deploy it and it misses every spike. The model didn't fail because the data was bad. It failed because the context window was wrong.
By the end of this piece you will be able to spot when a time-series model is silently truncating your input, choose a context length that matches your horizon, and wrap the call so failures surface instead of hiding in production.
The silent truncation trap
Time-series foundation models like Mistral's Timestral process inputs in fixed context windows. The window is the number of tokens the model can attend to at once. When your series is longer than the window, the model drops the oldest tokens.
No error is raised. No warning is logged. The forecast still runs. It runs on the tail of your data, and the output looks reasonable because these models are good at producing plausible-shaped curves from whatever input they receive.
This is not a bug in the model. It is a mismatch between your data length and the model's context size. The model has no way to know which part of the series you care about most. Only your code can enforce that.
The danger is that truncation is invisible at both ends. The request succeeds. The response parses. The forecast curve looks smooth. The only sign something went wrong is that your production metrics drift, and by then the bad forecasts have already shipped.
Measure before you model
Before calling any time-series model, check how many tokens your series produces. Most time-series models tokenize each timestep as one or more tokens, depending on the number of features and the precision of the values. A univariate hourly series and a multivariate minute-level series with the same row count can produce very different token counts.
Start with an estimate before you touch the model.
import numpy as np
def estimate_tokens(series, features_per_step=1, bytes_per_value=4):
# Rough estimate: each value becomes ~1 token after encoding
total_values = series.shape[0] * features_per_step
return int(total_values * bytes_per_value / 3)
data = np.random.randn(8760, 1) # one year of hourly data
print(f"Estimated tokens: {estimate_tokens(data)}")
This is an estimate, not a guarantee. Tokenization schemes differ between models and providers. Treat the number as an order-of-magnitude check. When you are close to the limit, check the provider's documentation for the exact tokenization rule, or send a small probe series and read the reported token usage from the response.
If your estimate exceeds the model's context window, you must downsample or chunk. Do not skip this step.
Chunk or compress
Two strategies work in practice. Pick based on whether your signal is stationary, meaning its statistical properties stay roughly constant over time.
| Approach | When to use | Tradeoff |
|---|---|---|
| Sliding window | Signal has repeating patterns | Loses long-term trends |
| Downsampling | Signal is noisy or high-frequency | Loses fine-grained detail |
For hourly data with daily cycles, a 24-step sliding window often preserves enough context. For yearly trends, downsampling to daily means keeps the shape but hides intraday spikes.
The choice matters more than it looks. If you downsample a series whose spikes are the thing you care about, you average away the signal you wanted to predict. If you chunk a series with a slow trend, each chunk forecasts from a different baseline and your outputs become inconsistent across chunks.
A sliding window keeps the most recent N steps and drops everything older.
def sliding_window(series, window_size):
if len(series) <= window_size:
return series
return series[-window_size:]
recent = sliding_window(data, 24)
print(f"Kept {len(recent)} of {len(data)} steps")
The window must be at least as long as the seasonal cycle you want the model to capture. A 24-step window on hourly data covers one day. It will not capture weekly patterns. Match the window to the cycle, not to the forecast horizon.
Wrap the call with guards
Never call a time-series model directly in production. Wrap it so truncation is visible.
import requests
def forecast_with_context_check(series, max_context=8192):
token_count = estimate_tokens(series)
if token_count > max_context:
raise ValueError(
f"Series needs {token_count} tokens, model only accepts {max_context}. "
"Downsample or chunk before calling."
)
# Actual model call would go here
return {"status": "ok", "tokens_used": token_count}
try:
result = forecast_with_context_check(data)
except ValueError as e:
print(f"Blocked: {e}")
The max_context value should come from the provider's current documentation, not from memory. Context limits change between model releases, and a hardcoded stale value will either block valid requests or quietly allow truncation again.
This guard turns silent truncation into a loud failure. You fix it in staging, not in production. The exception is intentional: a forecast built on partial data is worse than no forecast, because it looks trustworthy.
What still goes wrong
Even with the right context length, foundation models struggle with distribution shift. If your training data ends in January and you forecast July, the model has never seen that regime. It will interpolate confidently and be wrong.
Context length does not protect you from this. A longer window gives the model more history, but history does not help when the future behaves differently from the past.
Always validate on the most recent window of your actual data. Hold out the last stretch of the series, forecast it, and compare against the real values before you trust the model on anything new. Do not trust a model that has not seen your current conditions.
Key takeaways
- Time-series models truncate silently when your input exceeds their context window, and the forecast still looks plausible.
- Always estimate token count before calling the model and compare it to the documented context limit.
- Use sliding windows for repeating patterns and downsampling for noisy high-frequency data.
- Match the sliding window to the seasonal cycle you need, not to the forecast horizon.
- Wrap model calls with explicit guards so truncation becomes a loud failure, and validate on recent data to catch distribution shift.
Source
Timestral: Mistral's time-series foundation models -- I added working code for token estimation and sliding windows, a chunking strategy table, and a production guard pattern that surfaces truncation as a loud failure instead of a silent one.
Support this work
These write-ups are researched and published with no paywall, sponsor, or tracking. If one saved you an afternoon, a small tip keeps them coming.
USDT, USDC or USDD ยท TRC-20 (Tron)
TFTNsfyomKrnUutRjBTGVULp19ByW29KbY
Top comments (0)