SessionEnd hooks do not fire on normal `claude -p` (print mode) exit
Summary
SessionEnd hooks do not fire when a print-mode session (claude -p "...") exits normally. SessionStart and Stop hooks from the same settings file fire correctly in the same run, so this is specific to SessionEnd.
Environment
- Claude Code v2.1.216
- macOS (Darwin 25.5.0, arm64)
- Reproduced with project-scoped
.claude/settings.jsonhooks (no plugins required)
Reproduction
- Create an empty directory with
.claude/settings.json:
{
"hooks": {
"SessionStart": [{"hooks": [{"type": "command", "command": "touch $CLAUDE_PROJECT_DIR/marker-SessionStart"}]}],
"Stop": [{"hooks": [{"type": "command", "command": "touch $CLAUDE_PROJECT_DIR/marker-Stop"}]}],
"SessionEnd": [{"hooks": [{"type": "command", "command": "touch $CLAUDE_PROJECT_DIR/marker-SessionEnd"}]}]
}
}
- Run from that directory:
claude -p "reply with exactly: ok"
- After the process exits (exit code 0, output
ok), list the directory.
Observed: marker-SessionStart and marker-Stop exist; marker-SessionEnd was never created (checked repeatedly after exit).
Expected: per the hooks documentation, SessionEnd should run when the session ends (e.g., reason prompt_input_exit / other), in print mode as well.
Impact
Any plugin or configuration that relies on SessionEnd for resource teardown never runs it for headless sessions. Concrete case: a plugin that spawns a per-session helper daemon and registers a SessionEnd hook to shut it down (the OpenAI Codex companion plugin does exactly this) permanently leaks one daemon process group per claude -p invocation, since the daemon's only lifecycle owner is that hook. On an automation-heavy machine these leaked processes accumulate quickly.
Notes
- Interactive sessions were not part of this repro; the control condition (SessionStart/Stop firing from the same file) isolates the gap to SessionEnd dispatch in print mode.
- Related but distinct: #35976 covered SessionEnd behavior on
/resumein interactive sessions.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗