Your Agent Isn't Hallucinating — It's Reading From a Cache Nobody Invalidated
Day 3 — Context Window as Cache
Last week one of my engineers had an agent edit a caller to match a function signature — except a teammate had changed that signature ten turns earlier, and the agent never noticed. The edit compiled. It read as deliberate. It was wrong. Call that a hallucination and you'll misdiagnose it every time. It's a cache bug. And it's sitting inside every long agentic session you're running right now, completely unmonitored.
What Went Wrong
Turn 1: the agent reads auth.py to build a feature. It sees validate_token(token). Turn 4: a teammate ships validate_token(token, scope) — a new permissions model needs it. Nobody tells the agent to re-read the file, so it doesn't. Turns 5 through 9: it keeps reasoning about auth using the version it saw at turn 1 — the only version it has. Turn 10: it wires up a caller with validate_token(token), no scope, and writes a docstring explaining a contract that's now wrong. That's not a glitch. That's a coherent, internally consistent model of the code — just ten turns out of date.
The Reframe: It's a Cache, Not a Hallucination
'Hallucination' says the model invented something out of thin air. It didn't. It reasoned correctly over data that was correct at the time it was fetched — which is precisely what a stale cache read looks like. Map the vocabulary directly: the context window is the cache. Every file read, every grep, every 'here's the current state' tool call is a cache fill. Every turn that goes by without a re-read is staleness quietly accumulating on that entry — and nothing in the transcript flags it as aging.
Caching Vocabulary, Mapped Onto the Context Window
- ▹Hit rate — the agent 'remembers' the file with no tool call. Feels like competence. Reads exactly like competence. Can't be told apart from a stale hit until something breaks.
- ▹Eviction policy — there isn't one. Nothing ages an entry out. A signature read at turn 1 carries the same authority at turn 50 as one read at turn 49 — the model can't tell the difference.
- ▹Invalidation — nobody sends the signal. Redis has pub/sub and TTLs for exactly this, plus cache-aside as the standard pattern to keep things consistent with the source of truth. The context window has none of that — a teammate's commit doesn't push an event into the agent's window.
- ▹TTL — effectively infinite, until the window fills up and something gets summarized or dropped. That's not expiration by relevance. That's expiration by memory pressure, which is about the worst failure mode a cache can have.
Why This Is Worse Than a Stale Redis Read
A stale Redis read hands back wrong data, and wrong data is cheap to catch downstream — a null where you expected a value, a 500, a failed assertion. A stale context read gets reasoned over. The model builds a plausible plan on top of it, writes confident code, and then writes a confident explanation for why that code is correct. The failure isn't a crash — it's a well-argued wrong decision. And it's silent by design: nothing in the pipeline tells 'reasoning over fresh state' apart from 'reasoning over ten-turn-old state.' Both come out fluent and structured.
The Practical Tell
Here's the tell, and you can go find it in your own transcripts right now: scroll a long session and look for the gap between the last tool call that actually read a file or ran a command, and the turn where the agent makes a decision citing that file's contents. If it's confidently describing a function signature, a config value, a test's expected output — and there's no Read/Grep/Bash call backing that up in the last several turns — that's a stale cache read in progress, not memory. It's the same thing I'd flag reviewing anyone's reasoning trace: confident and wrong is more dangerous than hesitant and wrong, because it's so much more convincing.
What Actually Mitigates It Today
There's no invalidation hook to wire up yet. This is damage control, not a fix.
- ▹Force a re-read right before any edit to a file the agent hasn't touched this turn. Treat 'I'm about to change this' as the trigger — not 'I remember what's there.'
- ▹Keep sessions short for anything touching shared, actively-changing code. The shorter the session, the smaller the window for someone else's commit to quietly invalidate your agent's mental model.
- ▹Call /compact and context resets what they are: manual cache eviction. 'Let's compact before this edit' should carry the same weight on your team as 'let's flush the cache before this deploy' — a deliberate act of clearing staleness, not just tidying up the context window.
Tomorrow: Who Invalidates This Cache?
Once you see the context window as a cache, you can't stop seeing caches everywhere in agent design — retrieved docs in a RAG pipeline, a planner's memory of subagent state, a shared scratchpad in a multi-agent system. Every one of them carries the same open question: who invalidates this, and how do they even know to? That's tomorrow's concept.
Extend your knowledge
- ▹Go back to Day 1-2 of this series on tokens and context windows, then re-map them onto cache-fill and TTL from today — the vocabulary should click into place retroactively.
- ▹Pull your longest recent Claude Code transcript and mark, by hand, every Read/Grep/Bash call against every decision that cites file contents. Count the staleness gaps yourself — more convincing than anything I can tell you.
- ▹Look at how MCP resource subscriptions try to push change notifications for external state. It's the closest thing to a real invalidation hook that exists today — and it's still opt-in, per resource.
- ▹Reread the old line — 'there are two hard things in computer science: cache invalidation and naming things.' Today's lesson is that joke showing up inside your agent's context window, not just your infra.
Discussion
Chat with Chi Cong (AI) about this article. Your conversation is private to you — you can publish a summary for others when you're done.