[BUG] PreCompact hook exit code 2 does not block compaction
Preflight Checklist
- [x] I've searched existing issues and didn't find a match
- [x] I've read the documentation
- [x] I can reproduce this issue consistently
What's Wrong?
PreCompact hook returning exit code 2 does not block compaction. The hook executes and displays the stderr message, but compaction proceeds regardless.
Source-level cause: executePreCompactHooks() return type only includes newCustomInstructions and userDisplayMessage — the blocked flag from executeHooksOutsideREPL (which does detect exit code 2) is not propagated to the caller in compact.ts.
The hook output shows:
Compacted PreCompact [...] failed: ⛔ compact 금지. /clear 후 새 세션으로 진행하세요.
✻ Conversation compacted (ctrl+o for history)
The "failed" message appears but compaction still completes.
What Should Happen?
PreCompact hooks should support the same exit code 2 blocking semantics as PreToolUse hooks. When a PreCompact hook returns exit code 2, compaction should be blocked and the stderr message should be displayed to the user.
Steps to Reproduce
- Create a PreCompact hook script:
// block-compact.js
process.stderr.write("⛔ compact blocked.");
process.exit(2);
- Register it in
settings.json:
{
"hooks": {
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "node /path/to/block-compact.js"
}
]
}
]
}
}
- Run
/compactin a Claude Code session. - Expected: Compaction is blocked, message displayed.
- Actual: Message is displayed but compaction proceeds.
Use Case
- Enforce
/clearinstead of compact to maintain explicit session boundaries - Prevent context loss from automatic compaction, letting users decide when to transition sessions
Claude Model
claude-opus-4-6
Is this a regression?
I don't know
Claude Code Version
2.1.88 (Claude Code)
Platform
Other
Operating System
Windows 11 Home 10.0.26200
Terminal/Shell
PowerShell
Additional Information
executeHooksOutsideREPL correctly sets blocked=true for exit code 2, but executePreCompactHooks() does not include blocked in its return type, so the blocking signal is lost before reaching the compact logic.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗