The Context Tax: Why Your Coding Agent Read the Same 600 Lines 400 Times

·13 min read·Evergreen Tools Team
Developer terminal showing token-heavy agent session

💡 Tool TipOptimizing agent token spend? Measure real usage with AI Token Counter, and prepare files for agent context with Code to Markdown. AI Token Counter, Code to Markdown

When a coding agent works in a codebase that does not fit in its context window, it navigates the only way a shell can: grep, then read a file, then read a bigger slice of the file. SonarSource research engineer Antonio Aversa calls the result the context tax: every one of those reads stays in the conversation and is re-billed on every later turn. In his own repository, one ordinary ~800-line pull request added up to 156 million context tokens, a context window that peaked at 458,700 tokens, and roughly $41 -- for a change whose final diff a person could read in five minutes. Across 18 comparable PRs the average was ~234 million context tokens and ~$65 per PR. This is not one unlucky PR; it is a structural cost problem.

1. The Mechanism: Context Is a Tax You Pay Every Turn

A coding agent does not read a file once. On every new step, the model is re-sent the entire conversation so far as input. Prompt caching makes those repeated tokens cheap per unit (around 10% of the input price), but you still pay for them on every single turn. So the true cost of a token is not its size; it is its size times the number of turns it survives. Read a 600-line file on turn 40 of a 512-turn session and you have not paid for 600 lines -- you have paid for 600 lines times roughly 470 more turns. Every file read and tool result stays in the conversation and is re-sent until the run ends or the window is compacted, which is how cache-read tokens ballooned to 152.8 million on a single PR.

// The context tax, in one formula. A token's true cost is not
// its size. It is its size times the number of turns it
// survives in the conversation.
//   cost(token) = size(token) x turns(token)
// SonarSource measured one ~800-line PR in its own repo:
const MEASURED = {
  modelRoundTrips: 512,
  peakContextWindow: "458,700 tokens",
  freshInputTokens: "106k",
  cacheReadTokens: "152.8M",   // the re-billed transcript
  totalBilled: "~156M tokens",
  sessionCost: "~$41",
};
// The line that matters: cache-read = 152.8M tokens. Every
// read stays in the conversation and is re-sent every turn.

2. One Over-Read, Traced End to End

Early in that PR, the agent needed to understand a ~67-line helper function. To find it, it read the whole 618-line file (6,472 tokens) instead of the ~700 tokens it used: an immediate waste of ~5,770 tokens. That read entered the conversation around turn 42 and stayed for the remaining 470 turns. Re-billed as a cache read each turn at current cache-read pricing (about $0.20 per million), that is 5,770 times 470, roughly 2.7 million tokens -- about $0.54, for one unnecessary file read. The PR did this about 10 times, plus dozens of blind tree-wide greps, several of which returned nothing and forced a second, wider grep. The avoidable navigation overhead alone ran into several dollars on a single $41 PR, and it scales with the size of the repo, not the size of your change.

Circuit board representing token flows through context
// The mechanism, traced end to end. Early in that PR the agent
// needed a ~67-line helper inside a 618-line file. It read the
// whole file (6,472 tokens) instead of the ~700 tokens it used.
const ONE_OVER_READ = {
  wastedTokens: "~5,770",
  enteredConversation: "around turn 42",
  survivedTurns: 470,
  rebilledWaste: "5,770 x 470 ~= 2.7M tokens",
  costAt20CentsPerMillion: "~$0.54  // for ONE file read",
};
// The PR did this roughly 10 times, plus blind tree-wide greps,
// several of which returned nothing and forced a wider grep.
// "The cost scales with the size of the repo, not the size of
// your change." -- Antonio Aversa, SonarSource research eng.

3. Why Grep Fails Agents: The Binding Problem

During a refactor of SemSitter itself, the agent needed the most ordinary answer in programming: for the call ctx.method_index.resolve_return_type(...), what type is ctx.method_index, where is resolve_return_type defined, and what does it return? The honest answers: MethodIndex, method_index.rs, and Option<&str>. The agent had no index of its own, so it grepped and got a definition in every backend at once -- Python, TypeScript, Java, Rust, C#, and a shared core -- and the regex cannot say which one the call binds to. So it opened the file, read a wide slice, grepped for the helper, opened that file, and repeated. The irony is plain: the agent was building the exact capability it was missing, call-site resolution.

# The old navigation loop: grep, read, read a wider slice.
grep -rn "resolve_return_type" .
# -> a definition in EVERY backend at once (Python, TypeScript,
#    Java, Rust, C#, shared core). The regex cannot say which
#    one this call binds to.
open method_index.rs      # read a generous slice
grep -rn "extract_type_name" .   # and again, and again
# Every one of those reads is now permanently in the
# conversation, re-billed on every later turn.

4. The Way Out: Answer Navigation Questions from a Graph

Sonar's answer is Sonar Vortex with SemSitter, an in-house semantic navigation engine that keeps a local Unified Dependency Graph (UDG) of the repository updated instantly on every change. Instead of grep, then a slice, then a wider slice, the agent queries the graph for a specific node and gets back that node plus its typed relationships, including call sites a regex would not have matched. The measurable effects: less context carried per task, fewer round-trips, and correct answers in repositories larger than the window. For platform teams this points at a clear investment: give agents a semantic navigation layer instead of letting them grope through the codebase the way a shell does.

Code navigation graph replacing blind file reads

5. Measure Your Own Context Tax First

Before optimizing, measure: log the tokens added each turn (files read, tool output), the cache-read tokens billed that turn, and the running total with a projected session cost. Then apply levers cheapest first: include signatures or headers instead of whole files; prefer targeted navigation queries over grep; keep agent edits small, because every surviving token is a tax; and compact aggressively when windows approach the ceiling. Teams obsess over model choice and prompt length while the biggest line item often sits in the data formats and read strategies between the agent and the tools.

// The alternative: answer navigation questions from a graph
// instead of from raw file reads. Sonar's SemSitter engine
// keeps a Unified Dependency Graph updated on every change;
// the agent asks for a node and gets the node plus its typed
// relationships -- call sites a regex would never match.
{
  "query": "resolve_call",
  "call": "ctx.method_index.resolve_return_type(owner, method_name)",
  "graph_answer": {
    "receiver_type": "MethodIndex",
    "definition": "method_index.rs",
    "return_type": "Option<&str>",
    "call_sites_in_repo": 3
  }
}
// One targeted query replaces grep + whole-file reads and the
// agent carries far less context per turn.

6. An Action List for Your Team

First, export token bills from real sessions and find the cache-read share -- if it is above 90% like Sonar's measurement, you have enormous headroom. Second, pilot semantic navigation or a structured index on your largest repository and compare tokens and round-trips before and after. Third, write read-with-a-reason into your agent's working rules: before grepping, ask whether you need a definition, call sites, or a type. The context tax will not disappear, but measured and attacked with better navigation, it can drop from $65 per PR to a rounding error.

# Measure your own context tax before optimizing. Log per turn:
#   - tokens added this turn (files read, tool output)
#   - cache-read tokens billed this turn
#   - running total and projected session cost
def per_turn_tax(read_tokens: int, turns_remaining: int, cache_price_per_m: float = 0.20):
    return read_tokens * turns_remaining * cache_price_per_m / 1_000_000
# Remediation levers, cheapest first:
# 1. Include signatures/headers instead of whole files.
# 2. Prefer targeted navigation queries over grep.
# 3. Keep agent edits small; each surviving token is a tax.
# 4. Compact aggressively when windows approach the ceiling.

📌 Frequently Asked Questions

What is the context tax?

SonarSource's term for the cost pattern where every file read and tool output an agent pulls into a conversation is re-sent and re-billed on every later turn, so a token's real cost is its size times the number of turns it survives.

What is the context tax?

SonarSource's term for the cost pattern where every file read and tool output an agent pulls into a conversation is re-sent and re-billed on every later turn, so a token's real cost is its size times the number of turns it survives.

What is the context tax?

SonarSource's term for the cost pattern where every file read and tool output an agent pulls into a conversation is re-sent and re-billed on every later turn, so a token's real cost is its size times the number of turns it survives.

What is the context tax?

SonarSource's term for the cost pattern where every file read and tool output an agent pulls into a conversation is re-sent and re-billed on every later turn, so a token's real cost is its size times the number of turns it survives.

What is the context tax?

SonarSource's term for the cost pattern where every file read and tool output an agent pulls into a conversation is re-sent and re-billed on every later turn, so a token's real cost is its size times the number of turns it survives.

How big is the context tax in practice?

One measured 800-line PR burned ~156 million context tokens and about $41, with 152.8 million cache-read tokens; across 18 comparable PRs the average was ~234 million context tokens and ~$65 per PR.

How big is the context tax in practice?

One measured 800-line PR burned ~156 million context tokens and about $41, with 152.8 million cache-read tokens; across 18 comparable PRs the average was ~234 million context tokens and ~$65 per PR.

How big is the context tax in practice?

One measured 800-line PR burned ~156 million context tokens and about $41, with 152.8 million cache-read tokens; across 18 comparable PRs the average was ~234 million context tokens and ~$65 per PR.

How big is the context tax in practice?

One measured 800-line PR burned ~156 million context tokens and about $41, with 152.8 million cache-read tokens; across 18 comparable PRs the average was ~234 million context tokens and ~$65 per PR.

How big is the context tax in practice?

One measured 800-line PR burned ~156 million context tokens and about $41, with 152.8 million cache-read tokens; across 18 comparable PRs the average was ~234 million context tokens and ~$65 per PR.

Why are cache reads so expensive?

Prompt caching makes repeated tokens cheap per unit (about 10% of input price), but they are billed every turn: one wasted 5,770-token read that survives 470 turns adds up to roughly 2.7 million tokens.

Why are cache reads so expensive?

Prompt caching makes repeated tokens cheap per unit (about 10% of input price), but they are billed every turn: one wasted 5,770-token read that survives 470 turns adds up to roughly 2.7 million tokens.

Why are cache reads so expensive?

Prompt caching makes repeated tokens cheap per unit (about 10% of input price), but they are billed every turn: one wasted 5,770-token read that survives 470 turns adds up to roughly 2.7 million tokens.

Why are cache reads so expensive?

Prompt caching makes repeated tokens cheap per unit (about 10% of input price), but they are billed every turn: one wasted 5,770-token read that survives 470 turns adds up to roughly 2.7 million tokens.

Why are cache reads so expensive?

Prompt caching makes repeated tokens cheap per unit (about 10% of input price), but they are billed every turn: one wasted 5,770-token read that survives 470 turns adds up to roughly 2.7 million tokens.

How do I reduce the context tax?

Measure your bills first, then include signatures instead of whole files, replace grep with targeted semantic-graph queries, keep edits small, and compact aggressively near window limits.

How do I reduce the context tax?

Measure your bills first, then include signatures instead of whole files, replace grep with targeted semantic-graph queries, keep edits small, and compact aggressively near window limits.

How do I reduce the context tax?

Measure your bills first, then include signatures instead of whole files, replace grep with targeted semantic-graph queries, keep edits small, and compact aggressively near window limits.

How do I reduce the context tax?

Measure your bills first, then include signatures instead of whole files, replace grep with targeted semantic-graph queries, keep edits small, and compact aggressively near window limits.

How do I reduce the context tax?

Measure your bills first, then include signatures instead of whole files, replace grep with targeted semantic-graph queries, keep edits small, and compact aggressively near window limits.

Does semantic graph navigation really help?

Sonar's SemSitter answers navigation queries from a local Unified Dependency Graph, carrying far less context per task with fewer round-trips, and it finds call sites that regex cannot match.

Does semantic graph navigation really help?

Sonar's SemSitter answers navigation queries from a local Unified Dependency Graph, carrying far less context per task with fewer round-trips, and it finds call sites that regex cannot match.

Does semantic graph navigation really help?

Sonar's SemSitter answers navigation queries from a local Unified Dependency Graph, carrying far less context per task with fewer round-trips, and it finds call sites that regex cannot match.

Does semantic graph navigation really help?

Sonar's SemSitter answers navigation queries from a local Unified Dependency Graph, carrying far less context per task with fewer round-trips, and it finds call sites that regex cannot match.

Does semantic graph navigation really help?

Sonar's SemSitter answers navigation queries from a local Unified Dependency Graph, carrying far less context per task with fewer round-trips, and it finds call sites that regex cannot match.