← Back to PRs

#12195: fix(agents): sync config fallback for lookupContextTokens cold-start race

by mcaxtr open 2026-02-09 00:19 View on GitHub →
agents size: S trusted-contributor experienced-contributor
## Summary `lookupContextTokens()` uses a fire-and-forget async IIFE to populate `MODEL_CACHE`, but the cache lookup is synchronous. On first call — before async model discovery completes — the function returns `undefined`, causing all 12+ callers to fall back to `DEFAULT_CONTEXT_TOKENS` (200K). This affects: - **Status display**: Shows 200K instead of actual context window on cold start - **Session persistence**: Persists incorrect 200K value to sessions.json - **Cron sessions**: Consistently use 200K (never get the correct value) ### Fix Add a synchronous config-backed fallback cache (`CONFIG_CACHE`) that reads `contextWindow` from `models.providers.*.models[]` in the user's config file. The cache is hydrated lazily on first fallback lookup and reused for subsequent calls. Priority chain: `MODEL_CACHE` (async discovery) → `CONFIG_CACHE` (sync config) → `undefined` (caller provides DEFAULT_CONTEXT_TOKENS) ### Test plan - [x] Write failing test proving `lookupContextTokens` returns `undefined` on cold start (before fix) - [x] Test returns correct contextWindow from config for multiple providers - [x] Test returns `undefined` for unknown model IDs - [x] Test handles `undefined` and empty string model IDs - [x] All 5 new tests fail before fix, pass after - [x] `pnpm build` passes - [x] `pnpm check` passes Fixes #12158 <!-- greptile_comment --> <h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR fixes a cold-start race in `lookupContextTokens()` where the async model-discovery cache (`MODEL_CACHE`) is populated fire-and-forget, but the lookup is synchronous and can return `undefined` on first call. It adds a synchronous, lazily-hydrated fallback cache (`CONFIG_CACHE`) populated from the user config’s `models.providers.*.models[].contextWindow`, with priority `MODEL_CACHE → CONFIG_CACHE → undefined` (caller can then apply defaults). It also adds a focused unit test suite that mocks model discovery to stay empty and verifies the config-backed fallback returns the expected contextWindow for multiple providers, and returns `undefined` for unknown/empty/absent model IDs. <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with minimal risk. - The change is small and localized: it adds a deterministic synchronous fallback for a known cold-start race, and the new tests directly exercise the problematic startup path (async discovery empty) and expected fallback behavior. Previously raised issues about Vitest async mocking shape and config-cache population on transient load failures appear to have been addressed in the current commit. - No files require special attention <!-- greptile_other_comments_section --> <!-- /greptile_comment -->

Most Similar PRs