Desktop app persists session metadata before any user message → 'No messages yet' zombies clutter Recents
Summary
The Claude desktop app writes a local_*.json session-metadata file the moment a new session is opened, before any user message exists. If the user closes the window without typing, the metadata persists and the session shows up in the Recents sidebar forever — with a (often auto-generated or stale) title and No messages yet. in the body. Over time this is significant UI clutter.
Reproduction
- Open the Claude desktop app
- Click + New session in any project
- Wait ~3 seconds without typing
- Close the window / switch to another session
→ A new entry appears in the Recents sidebar with a title (sometimes pre-generated from project context, sometimes blank) and No messages yet. when reopened. It cannot be auto-cleared.
Evidence from one user's machine
Inspection of ~/Library/Application Support/Claude/claude-code-sessions/<UUID>/<UUID>/local_*.json files for a single project (Open-Jarvis):
Sessions sorted by (lastActivityAt - createdAt):
1.0s Implement crash reporter and recovery system
2.9s Check Jarvis binary version
8.6s Set up crash reporting and metrics collection
11.5s Resubmit awesome claude code
67.3s Research deliverable for Logos multi-faith search engine
...
112142.2s Fix mac-control-mcp bugs ← real 31-hour work session
943008.9s Cerebral Valley hackathon ← real 10.9-day work session
4 of 26 sessions (~15%) had lifetimes under 12 seconds — almost certainly never received a user message. Each local_*.json is 60–100 KB of metadata (mcp tool grants, model, permissionMode, etc.) but contains no messages array — the file only has session config, never message content.
$ jq 'keys' local_482754cc-...json
[ "alwaysAllowedReasons", "branch", "chromePermissionMode", "createdAt",
"cwd", "effort", "enabledMcpTools", "isArchived", "lastActivityAt",
"model", "originCwd", "permissionMode", "remoteMcpServersConfig",
"sessionId", "sessionPermissionUpdates", "sourceBranch", "title",
"worktreeName", "worktreePath" ]
The cwd of one zombie pointed at /Users/<user>/Open-Jarvis/.claude/worktrees/nifty-austin — a directory that no longer exists on disk. The session can never be resumed meaningfully, yet the entry stays in Recents.
Suggested fix
Don't persist the local_*.json until the first user message is committed. Hold it in memory only. On window close before any message: discard.
Alternative (less invasive): keep current behavior but auto-delete any local_*.json that has zero messages AND lastActivityAt - createdAt < N seconds AND createdAt > 1 hour ago. Same logic could run on app startup.
Current local workaround
Built a launchd agent that scans ~/Library/Application Support/Claude/claude-code-sessions/**/local_*.json every 5 minutes and moves zombies (lifetime < 30s AND age > 1h) to a .trash-zombies-<date>/ sibling directory. Reversible. Detection criteria are conservative — even a fast \"what is X?\" prompt takes >10s including response.
Logic in shell pseudo-code:
for f in claude-code-sessions/**/local_*.json; do
created=$(jq '.createdAt' "$f")
last_act=$(jq '.lastActivityAt' "$f")
lifetime=$((last_act - created))
age=$(( $(date +%s)*1000 - created ))
if [ $lifetime -lt 30000 ] && [ $age -gt 3600000 ]; then
mv "$f" .trash-zombies-$(date +%Y%m%d-%H%M%S)/
fi
done
Happy to share the full plist/script if useful.
Environment
- macOS (Apple Silicon)
- Claude desktop app: latest (2026-05-14)
- Claude Code CLI: 2.1.141
- Sessions affected: 4/26 (~15%) on my machine, accumulated over ~6 weeks
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗