Stop hook does not fire when the CLI is terminated by SIGTERM (e.g. external execution timeout)
Summary
When the claude process is terminated by SIGTERM — for example, by a wrapper enforcing an execution timeout via timeout — the configured Stop hook never runs. Any state the Stop hook is responsible for emitting is left stale (it still reflects the previous successful Stop), so an external supervisor cannot tell "completed" apart from "killed mid-work."
Environment
- Claude Code CLI 2.1.111
- Linux
- A
Stophook configured in.claude/settings.json
Expected
SIGTERM triggers a graceful-shutdown path that still fires the Stop hook (ideally with a flag/field indicating abnormal termination), or a dedicated termination hook fires, so downstream tooling can record that the run was killed rather than reading the prior run's hook output as if it were current.
Actual
SIGTERM kills the process with no Stop hook invocation. Downstream consumers that read the hook's output file see the previous run's content, with no signal that the current run was terminated.
Reproduction
- Configure a
Stophook that writes a marker file containing a fresh timestamp / run summary. - Run a prompt that takes longer than the timeout under a wrapper:
timeout 10 claude -p "<prompt that runs > 10s>"(or sendSIGTERMto theclaudePID manually). - Observe the marker file is not updated — it retains the prior run's content.
Impact
An external timeout supervisor cannot distinguish a clean completion from a mid-work kill. In our case a loop that had edited files mid-work surfaced a completely unrelated prior run's summary (e.g. "backlog confirmed empty — stopping"), because the only status available was the stale Stop-hook output from an earlier iteration.
Downstream workaround (for context, not a fix)
Our wrapper now writes an explicit timeout status itself when it observes exit code 124, before reading any hook output — precisely because the Stop hook cannot be relied upon on SIGTERM. This is a band-aid.
Request
Either fire the Stop hook on SIGTERM with a field indicating abnormal termination, or provide a dedicated signal-safe termination hook / flush so supervisors can record a killed run. Happy to provide more detail or test a fix.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗