Intermittent bug: Status line shows stale token count after /compact

Resolved 💬 2 comments Opened Oct 1, 2025 by secondcircle Closed Oct 1, 2025

Problem Summary

The status line intermittently shows stale (pre-compact) token counts after running /compact, while the /context command correctly shows the reduced post-compact count. The bug is not consistently reproducible.

Example observed behavior:

  • Pre-compact: Status line shows 146k tokens
  • Run /compact
  • Post-compact: /context shows 97k tokens (correct)
  • Post-compact: Status line still shows 146k tokens (stale)

Expected Behavior

After /compact completes:

  • Status line should immediately update to show reduced token count
  • Status line count should match (or be close to) /context count
  • Both should reflect the compacted conversation state

Actual Behavior

Occasionally after compact:

  • Status line continues displaying pre-compact token count
  • /context shows correct reduced count
  • Discrepancy persists across multiple status line refreshes
  • Bug does not always occur - appears intermittent

Investigation & Ruled Out Causes

Conducted systematic testing to identify root cause:

✅ Tested: session_id changes after compact

Hypothesis: Compact creates new transcript with new session_id, but status line hook receives old session_id

Test method:

  1. Modified .claude/settings.local.json to capture status line input:

``json
"statusLine": {
"command": "tee /tmp/statusline-input.json | ./.claude/statusline.sh"
}
``

  1. Captured JSON input before compact
  2. Ran /compact
  3. Captured JSON input after compact
  4. Compared session_id and transcript_path fields

Result: ❌ Not the cause

  • session_id remained identical before and after compact: d3da6f78-84d4-44b8-8e28-2189c1e9e2e6
  • transcript_path remained identical
  • Compact updated the transcript file in-place (same filename, updated mtime)
  • Status line correctly received the current session_id

✅ Tested: Sub-agent causing session_id confusion

Hypothesis: When Task tool (sub-agent) runs, it may have its own session_id that persists in status line after completion

Test method:

  1. Launched general-purpose sub-agent with simple task
  2. Monitored status line JSON input during agent execution
  3. Checked session_id after agent completed

Result: ❌ Not the cause

  • session_id remained identical during and after sub-agent execution
  • No evidence of sub-agent session_id "leaking" into parent session

✅ Tested: Compact behavior in controlled scenario

Test method:

  1. Established baseline: Status line 30k, /context 74k
  2. Ran /compact
  3. Measured post-compact: Status line 29k, /context 76k

Result: ❌ Bug did not reproduce

  • Both status line and /context updated correctly (small 1k reduction)
  • This test confirms the bug is intermittent, not constant

Possible Remaining Causes

Since the bug is intermittent and we've ruled out session_id issues:

  1. Auto-compact vs manual /compact: Bug may only occur with auto-compact
  2. Context size threshold: May only trigger at certain token counts (e.g., >150k)
  3. Multiple compacts in succession: Second/third compact may behave differently
  4. Timing/race condition: Status line hook called before transcript fully written/flushed
  5. Token calculator caching: File system or token calculator may cache results
  6. Transcript file locking: File may be locked/unavailable during compact operation

Diagnostic Infrastructure Added

To enable root cause analysis when bug occurs again, added comprehensive logging to status line script:

Log location: .claude/logs/statusline.log (JSON Lines format)

Each invocation logs:

{
  "timestamp": "2025-10-01T12:21:38-05:00",
  "session_id": "d3da6f78-84d4-44b8-8e28-2189c1e9e2e6",
  "transcript_path": "/home/ikme/.claude/projects/.../transcript.jsonl",
  "transcript_exists": true,
  "transcript_mtime": "2025-10-01 12:21:37",
  "token_count": "48",
  "calculation_success": true,
  "error": "null",
  "display_output": "⚡ • 48k/200k (24%) • Sonnet 4.5"
}

Log rotation: Automatically keeps last 10,000 lines to prevent unbounded growth

Captured data enables analysis of:

  • Whether session_id changed before/during/after compact
  • Whether transcript file existed and was readable
  • Whether token calculation succeeded or failed
  • Exact timestamp and modification time correlation
  • What output was displayed to user

Steps to Reproduce (If Known)

Unable to consistently reproduce. Bug occurs intermittently during normal usage with /compact.

To investigate further when bug occurs:

  1. Check .claude/logs/statusline.log for entries around compact time
  2. Compare session_id values before/after compact
  3. Check transcript_mtime to verify file was updated
  4. Look for calculation_success: false or error messages
  5. Compare token_count in log vs actual transcript tokens

Affected Components

  • [x] Status line display
  • [x] Token calculation
  • [ ] /compact command
  • [ ] /context command

System Information

  • Claude Code Version: 2.0.2
  • Operating System: Linux (WSL2)
  • Status Line Script: Custom .claude/statusline.sh using .claude/token-calculator.js

Additional Context

The status line uses a custom token calculator script that:

  1. Receives JSON input from Claude Code with session_id and transcript_path
  2. Reads the transcript file
  3. Calculates tokens using @anthropic-ai/tokenizer
  4. Displays result in status line

The /context command uses Claude Code's internal token tracking, which may explain why it's always correct while the status line (which reads the file directly) can be stale.

Request

Please investigate why the status line hook occasionally receives or calculates stale token counts after compact. The logging infrastructure is now in place to diagnose the issue when it reproduces.

---
Generated via workflow-support with comprehensive investigation

View original on GitHub ↗

This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗