context_window.used_percentage doesn't match internal context warning calculation

Status Open
Reported on v2.1.6
Maintainer reply None cached
Activity 5 comments · opened Jan 13, 2026

Description

The used_percentage and remaining_percentage fields added in v2.1.6 for status line input don't include system overhead (tool definitions, CLAUDE.md files, MCP server configs, system prompt). This causes a significant discrepancy between the reported percentage and Claude Code's internal "Context low" warning.

Steps to Reproduce

  1. Use Claude Code 2.1.6 with a custom status line that reads context_window.used_percentage
  2. Use the session until context gets high
  3. Compare used_percentage value with Claude's "Context low (X% remaining)" warning

Expected Behavior

used_percentage should match the percentage used in Claude Code's internal warning, so users can accurately track context usage.

Actual Behavior

| Source | Value |
|--------|-------|
| context_window.used_percentage | 75% |
| Claude Code warning | "Context low (6% remaining)" = 94% used |

19% discrepancy - the system overhead is not included in used_percentage.

Debug Data

{
  "total_input_tokens": 78446,
  "total_output_tokens": 28311,
  "context_window_size": 200000,
  "current_usage": {
    "input_tokens": 0,
    "output_tokens": 115,
    "cache_creation_input_tokens": 1793,
    "cache_read_input_tokens": 147730
  },
  "used_percentage": 75,
  "remaining_percentage": 25
}

current_usage total: 149,638 tokens = 74.8% ≈ matches used_percentage: 75

But Claude's warning says 6% remaining (94% used), meaning ~38,000 tokens of system overhead aren't accounted for.

Suggested Fix

Either:

  1. Include system overhead in used_percentage to match internal warning calculation
  2. Add a separate system_tokens field so status lines can calculate the true total
  3. Add a total_used_percentage field that includes everything

Environment

  • Claude Code version: 2.1.6
  • OS: macOS

View original on GitHub ↗

5 Comments

samjetski · 6 months ago

Could this be due to the Autocompact buffer? I.e. the remaining reported is correct in terms of the total unused context (100 - 100 * used / 200k), but the warning is showing percent until auto-compact (100 - 100 * used / (200k - 33k) in my case), not until the context is completely "full"?

<img width="499" height="219" alt="Image" src="https://github.com/user-attachments/assets/3bb76ab0-fbb7-455c-a7d6-48c77852dd43" />

If this is the case, the solution could be:

  • Expose the autocompact_buffer tokens value, and/or
  • Clarify the percentage property i.e. available_percentage vs remaining_percentage
jucor · 5 months ago

I keep being bitten by that issue. Would be really nice to have clarity, and ideally a fix :)

ramonmazinga · 5 months ago

Definitely not ideal. Keeps happening to me too. Would love a fix.

renchris · 4 months ago

Root Cause Analysis: The Three-Source Discrepancy

I've reverse-engineered the context calculation from cli.js (v2.1.7, verified through v2.1.91) and can confirm the exact mechanics behind the ~19% discrepancy reported here. It's actually larger than 19% in practice — I've measured up to 48 percentage points of divergence.

The Three Calculations

| Source | What It Counts | Denominator | Includes System Overhead | Includes Output Tokens |
|--------|---------------|-------------|------------------------|----------------------|
| Statusline used_percentage | Per-conversation input tokens | 200,000 | No (~38K excluded) | No |
| /context command | Per-conversation input tokens | 200,000 | No | No |
| Internal warning/auto-compact | Input + output tokens | ~123,000 (200K - buffers) | Yes | Yes |

Source Code Evidence

Statusline calculation — INPUT only:

// Counts: input_tokens + cache_creation_input_tokens + cache_read_input_tokens
// Does NOT count: output tokens, system prompt, tool definitions, CLAUDE.md

Warning/auto-compact calculation — INPUT + OUTPUT against a lower effective window:

effective_window = 200,000 - auto_compact_buffer (~13K) - output_buffer (~64K)
                 ≈ 123,000
trigger = (input_tokens + output_tokens) >= effective_window

Why the Gap Is ~48 Percentage Points

| Component | Tokens | % of 200K |
|-----------|--------|-----------|
| System overhead (prompt, tools, CLAUDE.md, MCP) | ~38,000 | 19% |
| Output token buffer | ~64,000 | 32% |
| Auto-compact reserve | ~13,000 | 6.5% |
| Total invisible to statusline | ~115,000 | ~48% |

In output-heavy sessions (code generation), the warning fires while the statusline shows ~52% used. The statusline is technically correct about input conversation tokens but meaningless for predicting when context will actually fill.

Proposed Fix

Expose in statusline JSON:

{
  "context_window": {
    "used_percentage": 47,
    "effective_used_percentage": 93,
    "system_overhead_tokens": 38000,
    "output_tokens_current": 64000,
    "effective_window": 123000
  }
}

This would let statusline scripts display the same number the internal warning uses, eliminating the discrepancy entirely.

Cross-References

This consolidates findings from:

  • #13783 (41 👍) — cumulative tokens partially fixed, but 15-20% gap persists (this is the system overhead)
  • #28167 — output tokens excluded from used_percentage
  • #15005 (9 👍, closed/locked) — "7% remaining" when actually 36% free
  • #12520 — original source code analysis with line-by-line evidence
  • #18241 — three-way mismatch documentation
  • #36014 — getEffectiveWindow() returns 200K instead of 1M on extended context
junaidtitan · 3 months ago

The three-source divergence documented in this thread is the reason a measured number off the JSONL is more trustworthy than any single statusline figure — the transcript records actual per-turn input/output/cache usage, so you can compute "real" consumption directly instead of reconciling three formulas. If it's useful while this gets fixed, cozempic does exactly that read-only: cozempic diagnose <session-id> (via uvx cozempic) prints measured token usage from the session file. It won't change CC's displayed %, but it gives you a ground-truth number to compare against.