[BUG] Infinite CPU loop on startup when session .jsonl contains duplicate saved_hook_context UUIDs
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
Claude Code freezes permanently (100% CPU, completely unresponsive) when launching in a directory whose project session cache (~/.claude/projects/<project>/) contains a .jsonl file with duplicate saved_hook_context entries that share the same UUID but have different parentUuid values.
This creates a cycle in the message parent chain that the session loader follows indefinitely:
hook_ctx(uuid=X, parent=A) → user(uuid=B, parent=X) → hook_ctx(uuid=X, parent=B) → user(uuid=B, parent=X) → ...
In practice, this happens when a plugin (e.g., "superpowers") writes saved_hook_context entries reusing the same UUID across conversation turns. Once a session with this pattern exists, the directory becomes permanently unusable — and every frozen session creates another orphaned .jsonl with the same problem, making it self-perpetuating.
What Should Happen?
- The session loader should detect cycles in the message parent chain (e.g., visited-set or max-depth limit) and gracefully skip or discard malformed sessions instead of looping forever.
- The hook context writer should assign unique UUIDs per
saved_hook_contextentry rather than reusing them across turns.
Error Messages/Logs
No error messages are produced. The process silently spins at 100% CPU with zero I/O. In debug logs, the API request is made but "Stream started" is never logged — the process hangs before receiving any response.
Steps to Reproduce
# 1. Create a minimal git repo
mkdir /tmp/claude-bug-repro && cd /tmp/claude-bug-repro && git init
# 2. Create the project session directory
mkdir -p ~/.claude/projects/-tmp-claude-bug-repro
# 3. Write a minimal .jsonl with a UUID cycle
python3 << 'PYEOF' > ~/.claude/projects/-tmp-claude-bug-repro/deadbeef-0000-0000-0000-000000000000.jsonl
import json
hook_uuid = "aaaa0000-0000-0000-0000-000000000003"
user2_uuid = "aaaa0000-0000-0000-0000-000000000005"
session_id = "deadbeef-0000-0000-0000-000000000000"
lines = [
{"type": "file-history-snapshot", "messageId": "aaaa0000-0000-0000-0000-000000000001",
"snapshot": {"messageId": "aaaa0000-0000-0000-0000-000000000001",
"trackedFileBackups": {}, "timestamp": "2026-01-31T12:00:00.000Z"}, "isSnapshotUpdate": False},
{"parentUuid": None, "isSidechain": False, "userType": "external",
"cwd": "/tmp/claude-bug-repro", "sessionId": session_id, "version": "2.1.27",
"type": "user", "message": {"role": "user", "content": "hi"},
"uuid": "aaaa0000-0000-0000-0000-000000000002", "timestamp": "2026-01-31T12:00:01.000Z"},
{"type": "saved_hook_context", "uuid": hook_uuid, "content": ["hook context"],
"hookName": "UserPromptSubmit", "parentUuid": "aaaa0000-0000-0000-0000-000000000004",
"sessionId": session_id, "isSidechain": False},
{"parentUuid": hook_uuid, "isSidechain": False, "userType": "external",
"cwd": "/tmp/claude-bug-repro", "sessionId": session_id, "version": "2.1.27",
"type": "user", "message": {"role": "user", "content": "Implement this plan:\n" + "x" * 18000},
"uuid": user2_uuid, "timestamp": "2026-01-31T12:00:02.000Z"},
{"type": "saved_hook_context", "uuid": hook_uuid, "content": ["hook context"],
"hookName": "UserPromptSubmit", "parentUuid": user2_uuid,
"sessionId": session_id, "isSidechain": False},
{"parentUuid": hook_uuid, "isSidechain": False, "userType": "external",
"cwd": "/tmp/claude-bug-repro", "sessionId": session_id, "version": "2.1.27",
"type": "assistant", "message": {"role": "assistant",
"content": [{"type": "text", "text": "I'll help with that."}]},
"uuid": "aaaa0000-0000-0000-0000-000000000006", "timestamp": "2026-01-31T12:00:03.000Z"},
]
for line in lines:
print(json.dumps(line))
PYEOF
# 4. Launch Claude Code and type "hi"
cd /tmp/claude-bug-repro && claude
# → Process spins at 100% CPU, never shows a response
# Cleanup:
# rm -rf ~/.claude/projects/-tmp-claude-bug-repro /tmp/claude-bug-repro
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.27
Platform
Anthropic API
Operating System
Other Linux
Terminal/Shell
Other
Additional Information
Workaround: Delete orphaned .jsonl files in ~/.claude/projects/<project>/ that are not referenced in sessions-index.json. Note that each frozen session creates a new orphaned .jsonl, so the problem is self-perpetuating until cleaned up.
How this occurs naturally: The "superpowers" plugin (and potentially other plugins) writes saved_hook_context entries that reuse the same UUID across multiple conversation turns with different parentUuid values. After a multi-turn session with such a plugin, the resulting .jsonl contains the cycle pattern described above. The next time Claude Code starts in that directory, it freezes.
Environment: Linux (Manjaro 6.18.4), Node.js v25.2.1
This issue has 5 comments on GitHub. Read the full discussion on GitHub ↗