DEV Community

Sungwoo Lee
Sungwoo Lee

Posted on Originally published at my-blog.org

Why Idle Claude Code Sessions Burn Tokens: 4.7 Turns per Alert

Your Claude Code session can lose tokens while you are away from the keyboard. The reason is simple: every request re-sends the whole conversation, so anything that creates a new turn costs as much as a turn you typed yourself. In one session we measured, a single cross-session alert triggered 4.7 turns on average. Here are the five things that open turns in an idle session, how to measure them in your own logs, and what to change.

The cost model in one line

Each request carries the full context. So a session's cost is roughly:

session cost ~ average context size x number of turns
Enter fullscreen mode Exit fullscreen mode

You control the first factor with /clear and by splitting work. The second factor is where idle sessions leak: the system can create turns when you are not typing. In a representative session from our logs (471 requests, about 458k tokens of average context), the cache-read total was 216.5M tokens against 0.43M output tokens. The product of context and turns explained almost all of it.

Five ways an idle session gets new turns

Cache lifetime is 1 hour on a subscription and 5 minutes with an API key or usage credits

  1. The first message after the cache expires. The prompt cache has a limited lifetime. On a subscription it is 1 hour; with an API key or usage credits the default is 5 minutes. Your first request after that gap re-processes the full context.
  2. Scheduled tasks such as /loop. A loop fires on its interval even when you are not working, and each firing sends the full context.
  3. Messages from another session. If you run several sessions that talk to each other, a message landing in an idle session becomes a new turn there.
  4. Goal check-ins. A goal that is waiting on background work opens status-check turns. The original post reports a cap of 3 check-ins per goal between your prompts, introduced in v2.1.246; before that there was no limit.
  5. Background-task and watcher notifications. A completion notice, or one line of output from a watch script, wakes the session for a turn each.

The cache expiry is the biggest single line item

We scanned 107 conversation transcripts from one project. Cache-expiry rewrites were the number one source of waste at 11.8%. The largest single rewrite was 778,949 tokens, after a gap of 10.6 hours.

The practical rule: if you are coming back after more than an hour, /clear and restart with a short brief instead of typing a follow-up into the old thread. A fresh start is free, and the old context would have been re-written in full anyway. The transcript scan behind these numbers has the method and the caveats.

One alert, 4.7 turns

In one session 15 alerts triggered 70 requests, about 4.7 per alert, plus 4 cache-expiry rewrites

Alerts are the cause people underestimate, because each one looks tiny. A notification arrives, and the chain that follows is: the session wakes, reads it, calls a tool, reads the result, and replies. Every step is a full-context request.

In one session on 2026-08-23 (172 requests after de-duplication):

  • 15 alerts arrived from other sessions.
  • Those alerts triggered 70 requests, which is 70 / 15, about 4.7 turns per alert.
  • The same session had 4 cache-expiry rewrites totaling 619,876 tokens.
  • Alert-triggered requests made up 40.9% of that session's cost.

Compared head to head, 2 alerts cost about as much as one cache rewrite. If a watcher pings you on every item in a 50-item batch, you have bought 50 wake-ups, not one.

One more trap: if the alert text is truncated (ours was cut at 200 characters), the session often spends an extra turn reading the rest. Check that your notifier sends the whole message.

Measure your own sessions

A session file had 463 assistant lines but only 218 unique request IDs

Session logs are JSONL files under ~/.claude/projects/. Lines can be huge, so parse them as JSON line by line; do not grep them with wide regexes. Two details matter. First, one request can span several assistant lines, so de-duplicate by requestId. On 2026-10-10 one of our files had 463 assistant lines but only 218 unique request IDs; skipping de-duplication more than doubles your numbers. Second, count a request as an expiry rewrite when the gap from the previous request exceeds the cache lifetime.

import json, sys
from datetime import datetime

ttl = 3600  # seconds; use 300 for API key / usage credits
seen, reqs = set(), []
with open(sys.argv[1], encoding="utf-8") as f:
    for line in f:
        try:
            d = json.loads(line)
        except ValueError:
            continue
        if d.get("type") != "assistant":
            continue
        rid = d.get("requestId")
        usage = (d.get("message") or {}).get("usage") or {}
        if not rid or rid in seen or "timestamp" not in d:
            continue
        seen.add(rid)
        ts = datetime.fromisoformat(d["timestamp"].replace("Z", "+00:00"))
        reqs.append((ts, usage.get("cache_creation_input_tokens", 0)))

reqs.sort()
rewrites = [c for (a, _), (b, c) in zip(reqs, reqs[1:])
            if (b - a).total_seconds() > ttl]
print(f"requests={len(reqs)} expiry_rewrites={len(rewrites)} tokens={sum(rewrites)}")
Enter fullscreen mode Exit fullscreen mode

This infers expiry from the time gap alone. It does not verify that the cache really expired, so treat the output as an estimate.

Cut the idle spend

In rough order of impact:

  • Run /clear when the topic changes.
  • Run /clear before resuming after a long break instead of continuing the old thread.
  • Check the Loops entry in /usage for scheduled tasks that keep firing while you are idle.
  • Hold cross-session messages that do not need an immediate answer.
  • Make watchers wake the session once, on completion or on abort, rather than on every success line.
  • Make sure alerts include the full text so no extra turn is needed to read the rest.

The full write-up, including the cost-model derivation and the transcript scan, is in the original post on my-blog.org.


Free tools I keep on my own site: 34 calculators and how-to guides - loans, severance pay, take-home salary, savings vs deposits. New ones go out in one email, no ads.

Top comments (0)