#17414: fix(sessions): refresh contextTokens when model override changes
stale
size: XS
Cluster:
Context Window Fixes
## Summary
- When `applyModelOverrideToSessionEntry()` switches the active model,
`entry.contextTokens` retains the previous model's context window
- Every downstream consumer — compaction threshold, memory flush, session
persistence, `/status` display — sees the stale value
- Fix: call `lookupContextTokens()` after the override is applied and write
the result back to `entry.contextTokens`
- Also handles the revert-to-default case by deleting `contextTokens` so it
gets re-resolved from config
## Problem
Switching models mid-session via `/model` (or alias, failover, quota
exhaustion) leaves `entry.contextTokens` set to the previous model's value.
For example, switching from Sonnet 4.5 (1M context) to Opus 4.6 (200K)
leaves the session believing it has 1M tokens of headroom. Compaction never
fires, and the session grows until the API rejects it with a context-length
error.
## Root Cause
`applyModelOverrideToSessionEntry()` in `src/sessions/model-overrides.ts`
updates `entry.modelOverride` and `entry.providerOverride` but does not
touch `entry.contextTokens`:
```ts
if (selection.model !== entry.modelOverride) {
entry.modelOverride = selection.model;
updated = true;
}
// contextTokens is never refreshed here
```
The `??` fallback chain at most callsites reads `entry.contextTokens` first:
```ts
entry?.contextTokens ?? lookupContextTokens(model) ?? DEFAULT_CONTEXT_TOKENS
```
Since `entry.contextTokens` is already set (from the previous model), the
fallback to `lookupContextTokens()` never triggers.
## Fix
After detecting that the model override was `updated`, call
`lookupContextTokens(effectiveModel)` and write the fresh value back to
`entry.contextTokens`. When the override is reverted (no effective model),
delete `contextTokens` so it gets re-resolved from config.
```ts
if (updated) {
const effectiveModel = entry.modelOverride;
if (effectiveModel) {
const fresh = lookupContextTokens(effectiveModel);
if (typeof fresh === "number" && fresh > 0) {
entry.contextTokens = fresh;
}
} else {
// Reverted to default — clear so it gets re-resolved from config.
delete entry.contextTokens;
}
entry.updatedAt = Date.now();
}
```
Fixes #8240
## Testing
- Verified that `/status` displays the correct context window after
switching models via `/model` alias
- Verified that reverting to the default model clears the stale value
- Existing tests pass
## AI Disclosure
This PR was authored with AI assistance.
Generated with [Claude Code](https://claude.com/claude-code)
<!-- greptile_comment -->
<h3>Greptile Summary</h3>
This PR fixes a critical bug where switching models mid-session via `/model` left `entry.contextTokens` set to the previous model's context window size. The fix introduces a `modelChanged` flag to track model/provider changes separately from auth profile changes, then refreshes `contextTokens` by calling `lookupContextTokens()` when the model changes. When reverting to default, it properly clears `contextTokens` so it gets re-resolved from config.
- Addresses the efficiency concern from the previous review thread by only refreshing `contextTokens` when model/provider actually changes, not on profile-only updates
- Prevents compaction failures and context-length API errors that occur when session believes it has more headroom than it actually does
- Handles both model switch and revert-to-default cases correctly
<h3>Confidence Score: 5/5</h3>
- This PR is safe to merge - it fixes a critical context window bug with a well-targeted solution
- The fix is surgical and correct: introduces a separate `modelChanged` flag to distinguish model changes from profile-only changes, refreshes `contextTokens` only when needed, and properly handles both model switch and revert cases. The logic is straightforward with appropriate guard conditions (type and value checks before setting `contextTokens`). This directly addresses the root cause described in the PR without touching unrelated code paths.
- No files require special attention
<sub>Last reviewed commit: 90c66f3</sub>
<!-- greptile_other_comments_section -->
<!-- /greptile_comment -->
Most Similar PRs
#23299: fix(status): show runtime model context limit instead of stale sess...
by SidQin-cyber · 2026-02-22
84.5%
#18886: fix(status): prefer configured contextTokens over model metadata
by BinHPdev · 2026-02-17
84.1%
#16478: fix(gateway): fall back to lookupContextTokens on model switch
by colddonkey · 2026-02-14
83.9%
#15726: fix(sessions): use model contextWindow instead of agent contextToke...
by lailoo · 2026-02-13
83.8%
#23136: fix: lookupContextTokens should handle provider/model refs
by patchguardio · 2026-02-22
82.6%
#17604: fix(context): use getAvailable() to prevent cross-provider model ID...
by aldoeliacim · 2026-02-16
81.5%
#14744: fix(context): key MODEL_CACHE by provider/modelId to prevent collis...
by lailoo · 2026-02-12
81.4%
#21847: fix(session): /new and /reset no longer carry over model overrides
by hydro13 · 2026-02-20
81.2%
#22277: fix: prevent heartbeat model override from bleeding into main session
by zhangjunmengyang · 2026-02-21
81.0%
#16609: fix: resolve session store race condition and contextTokens updates
by battman21 · 2026-02-14
81.0%