Claude Code Zombie Sessions: How --resume Burns Your Quota

Your Claude Code process survived a WSL disconnect and burned 74% of your quota overnight. Here's how to find zombie sessions, kill them, and stop it from happening again.

If your Claude Code usage spiked and you weren't actively working, a zombie background process is likely responsible. The --resume flag — combined with WSL's non-standard process cleanup behavior and a regression in extension versions v2.1.120 through v2.1.122 — can leave claude processes running detached, making API calls against your quota with no UI feedback. This guide covers how to find them, kill them, and prevent them.


TL;DR: The Claude Code --resume flag can cause background processes to outlive a WSL disconnect, silently consuming your API quota. The v2.1.120–2.1.122 extension regression on WSL made this worse by re-attaching to orphaned sessions automatically on auto-update, creating two parallel processes running simultaneously. Audit with pgrep -la claude, kill orphans with pkill -f "claude.*--resume", and add a session precondition check to your shell profile.


The Incident: 74% of Quota, Gone Overnight

In a thread on r/claude titled "Beware Claude's Resume Functionality," a developer reported:

"I refreshed the page and all the sudden I've used 74% of quota. The claude process survived the WSL disconnect and kept running in background."

This isn't a one-off edge case. It's a predictable outcome when three separate behaviors interact: WSL's process lifecycle, the --resume flag's session re-attachment behavior, and the extension auto-update regression. Developers running long async remote sessions — exactly the use case where --resume is most appealing — are the most exposed.

A parallel pattern surfaced in r/ClaudeCode from developers managing 10–12 concurrent tmux sessions on remote servers: more concurrent sessions means more surface area for orphaned processes to accumulate undetected.

What Is the --resume Flag?

The --resume flag instructs Claude Code to reconnect to an existing session using a session ID stored on disk — specifically, in the .jsonl transcript files under ~/.claude/projects/<encoded-cwd>/. The agent reloads context from the transcript and continues from where it left off.

The VS Code extension uses --resume automatically when it detects a prior session for the current working directory. This automatic re-attachment is what makes it convenient for resuming work — and what makes it dangerous when a prior session is still running.

Root Cause: Three Failure Modes That Compound

The zombie session problem isn't a single bug. It's three behaviors combining in a bad order:

1. WSL Doesn't Send SIGHUP on Disconnect

On a standard SSH disconnect, the OS sends SIGHUP to the foreground process group, cleaning up child processes. WSL doesn't do this reliably. When a WSL terminal closes or a network event interrupts the session, the claude CLI process can survive in a detached state — no terminal, no active connection, but still executing its current task and making API calls.

If you've read about how Claude Code processes respond to terminal close events, the key difference here is that WSL's behavior resembles Windows task scheduling more than Linux process groups — the process keeps running until explicitly stopped, not until its parent dies.

2. The Extension Auto-Update Race Condition (v2.1.120–2.1.122)

The v2.1.120 through v2.1.122 extension builds introduced a regression specific to WSL environments: on auto-update, the extension re-reads the working directory's session state and automatically invokes --resume with the most recent session ID.

If a background claude process from the disconnect above was still running, this created two parallel processes both attached to the same session — both making API calls, both counting against quota, neither surfaced in any UI. The auto-update timing made this nearly invisible: the user sees the extension reload, assumes it restarted cleanly, and walks away while two processes burn through tokens.

3. No Built-In Session Visibility

There is no claude ps, claude sessions list, or equivalent command. The only way to discover orphaned processes is through Unix process inspection. Most developers don't check until quota usage looks wrong.

How to Audit for Orphaned Claude Processes

# List all running claude processes with full arguments
pgrep -la claude

# Show PID, elapsed time, and full command line
ps -o pid,etime,args -p $(pgrep claude 2>/dev/null | tr '\n' ',')

# Grep without self-inclusion
ps aux | grep '[c]laude'

The [c]laude bracket trick prevents the grep process itself from appearing in results — a common false positive otherwise.

The etime column is the key signal: a process running for hours when you haven't been at your desk is a strong indicator of a zombie session. Look for processes with --resume <session-id> in the arguments — that's the flag that identifies sessions attached to prior transcripts.

How to Kill Orphaned Sessions

Once you've identified zombie processes:

# Kill a specific PID
kill <pid>

# Force kill if SIGTERM is ignored
kill -9 <pid>

# Target only resume-flag sessions, preserve any intentionally open sessions
ps aux | grep '[c]laude.*--resume' | awk '{print $2}' | xargs kill

# Nuclear option: kill all claude processes
pkill claude

After killing, wait 10–15 seconds and rerun pgrep -la claude to confirm no processes restarted (the extension may re-launch one automatically if auto-resume is still enabled).

How to Verify the Quota Impact

After cleanup, check console.anthropic.com — the Usage dashboard shows daily token consumption. Look for spikes on days you weren't actively working.

Session transcripts provide a more precise audit trail. Each turn is logged with a timestamp:

# Find recently-modified session transcripts (last 2 hours)
find ~/.claude/projects -name "*.jsonl" -mmin -120

# View the last few turns with timestamps in a specific session
tail -20 ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl | python3 -m json.tool

# Count turns in a session (proxy for API calls made)
wc -l ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl

Turns logged after your last known activity confirm background execution. A zombie session that ran for hours may also have modified files — run git diff HEAD in the affected repo and treat any unexpected changes as requiring review.

For a systematic approach to reviewing what an agent did after an unmonitored session, see How to Audit What Your AI Agent Actually Did After the Session. An independent Claude audit noted finding zombie processes holding authenticated API sessions with credentials persisted in session transcripts — another reason to review .jsonl files after discovering an orphaned session.

Prevention: Stop This From Happening Again

Add Shell Aliases

# ~/.zshrc or ~/.bashrc

# Inspect running claude sessions
alias claude-ps='ps -o pid,etime,args -p $(pgrep claude 2>/dev/null | tr "\n" ",")'

# Kill orphaned resume sessions only
alias claude-clean='ps aux | grep "[c]laude.*--resume" | awk "{print \$2}" | xargs kill 2>/dev/null && echo "Done"'

Add a Precondition Wrapper

For developers running multiple concurrent sessions — a common pattern for async agent workflows:

function claude-safe() {
  local count=$(pgrep -c claude 2>/dev/null || echo 0)
  if [ "$count" -gt 0 ]; then
    echo "Warning: $count existing claude process(es) detected:"
    pgrep -la claude
    read -p "Continue anyway? [y/N] " confirm
    [[ "$confirm" =~ ^[Yy]$ ]] || return 1
  fi
  claude "$@"
}

This intercepts every claude invocation and warns if prior sessions are running before proceeding.

Disable Extension Auto-Attach

In VS Code settings, disable automatic session resumption — especially critical in WSL:

{
  "claude.autoResumeSession": false,
  "claude.resumeOnReconnect": false
}

Check the current Claude Code extension settings page in VS Code for exact option names, as they shift between versions.

Resume Explicitly, Not Automatically

When reconnecting to a session deliberately, check first:

pgrep -la claude && echo "WARNING: active process detected" || claude --resume <session-id>

Never use --resume reflexively. Check that the prior session is actually terminated before re-attaching.

For multi-agent setups managing many concurrent sessions, tools like CCB (claude_codex_bridge) and gastown add structured zombie-session cleanup and token usage instrumentation at the orchestration layer — useful if you've scaled past what shell aliases can manage.

How Grass Eliminates This Problem Architecturally

The zombie session failure mode has a specific shape: a process running on your local machine outlives the connection it was started from. The mitigations above patch around that shape — detection, cleanup, auto-resume guards. But the root cause stays: session lifetime is tied to local process lifetime on a machine that disconnects, sleeps, and updates its extensions without your input.

Grass is built on a different architecture. With Grass, your Claude Code session runs on an always-on cloud VM — not on your laptop or WSL instance. When your laptop sleeps, your WSL terminal closes, or the VS Code extension auto-updates, none of that touches the server-side process. The agent runs in a controlled, managed server environment.

Failure mode Local process (laptop/WSL) Grass cloud VM
Laptop sleeps Session may survive as zombie Session runs normally on server
WSL disconnects Process survives detached No local process to orphan
Extension auto-update Risk of duplicate re-attach Server session is authoritative
Session visibility pgrep and inference Active sessions visible in mobile app
Quota audit Manual transcript inspection Session-scoped server tracking

Grass sessions are explicitly scoped to a sessionId and repoPath — the server tracks what's active. There's no ambiguity about what's running. The mobile app surfaces the current session state in real time, and if something looks wrong, you abort from your phone.

For developers already running long async Claude Code sessions — the exact workflow where --resume feels necessary — this is the architectural fix: move the session off the machine that's vulnerable to disconnects. Learn how to run Claude Code unattended on a persistent VM for the full setup, or get started with Grass in under 5 minutes to see what server-side session management looks like in practice.

To try it: npm install -g @grass-ai/ide, run grass start in your project directory, scan the QR code from your phone. Your session runs on the VM. Close your terminal, reopen it, reconnect — no orphaned processes, no surprise quota drain.


FAQ

How do I find zombie Claude Code sessions running in the background?

Run pgrep -la claude to list all active Claude Code processes with their full arguments. Processes showing a --resume <id> flag that you didn't intentionally start are likely orphaned. For elapsed time, use ps -o pid,etime,args -p $(pgrep claude | tr '\n' ',') — a process running for hours during off-hours is the clearest signal.

Why did my Claude Code quota suddenly drop even though I wasn't using it?

The most likely cause is an orphaned background session — a claude process that survived a WSL disconnect or terminal close and kept executing against the API. Check ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl for turns timestamped after your last known usage. Also check the Anthropic console for daily usage spikes.

Is the Claude Code --resume flag safe to use?

--resume is safe when you control when it runs. The risk is in automatic invocation: extension versions v2.1.120–2.1.122 on WSL would invoke --resume on auto-update, sometimes against a session that was still running, creating duplicate parallel processes. Mitigate by disabling auto-resume in extension settings and checking pgrep -la claude before resuming manually.

How do I stop Claude Code from consuming API tokens in the background?

Run ps aux | grep '[c]laude.*--resume' | awk '{print $2}' | xargs kill to target resume-flag sessions specifically, or pkill claude to stop all Claude processes. After killing, verify with pgrep -la claude. Then disable auto-resume in the VS Code extension settings to prevent re-occurrence.

How can I get visibility into active Claude Code sessions across multiple repos?

Session transcripts live at ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl. Run find ~/.claude/projects -name "*.jsonl" -mmin -120 to list sessions active in the last two hours. For structured multi-session observability with token instrumentation and orphan cleanup, tools like gastown add a dedicated monitoring layer — useful if you're running more sessions than shell aliases can manage.


Published by Grass — a machine built for AI coding agents. One always-on cloud VM where Claude Code, Codex, and Open Code run as first-class citizens, accessible from your laptop, phone, or automation.