claude-code - 💡(How to fix) Fix [FEATURE] Add UserPromptCancel / Stop-on-cancel hook event [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
anthropics/claude-code#45289Fetched 2026-04-09 08:08:49
View on GitHub
Comments
1
Participants
2
Timeline
3
Reactions
0
Timeline (top)
labeled ×2commented ×1

Fix Action

Workaround

Implement a reconciliation loop that detects stale "working" states (e.g. working for >10min with no live Claude process) and resets them. This is O(N) polling every 60s rather than an O(1) event. It also has a 60s delay before the indicator clears, which is jarring.

RAW_BUFFERClick to expand / collapse

Problem

When a user cancels a prompt mid-execution (Ctrl+C), Claude Code does not fire the Stop hook event. The session continues (not ended), but any hook-driven state that was set on UserPromptSubmit (e.g. a "working" indicator) is permanently stuck until the next Stop fires naturally.

This affects:

  • Status indicator systems (tmux pane indicators, statusline integrations)
  • Any hook-driven state machine that tracks Claude's activity state

The Stop event docs say it fires "when Claude stops responding", but cancellation via Ctrl+C is explicitly NOT handled.

Proposed Solution

Add a new hook event UserPromptCancel (or fire the existing Stop event on cancel):

EventTrigger
UserPromptCancelUser pressed Ctrl+C to cancel the current prompt
Stop (extended)Also fire on cancel, same as normal completion

The UserPromptCancel approach is preferred — it lets hook scripts distinguish cancellation from natural completion if needed.

Workaround

Implement a reconciliation loop that detects stale "working" states (e.g. working for >10min with no live Claude process) and resets them. This is O(N) polling every 60s rather than an O(1) event. It also has a 60s delay before the indicator clears, which is jarring.

Impact

This is a correctness gap in the hook event model. The hook lifecycle currently has no way to know when a user cancels, making hook-driven state machines unable to maintain accurate state after cancellation. The workaround is brittle and adds unnecessary complexity.

Related

  • PreToolUse fires during Claude's init sequence (loading plugins), causing premature "working" state before any user interaction. A startup guard is needed as a second workaround. A UserPromptStart event (after the prompt is actually ready to receive input) would also help here.

extent analysis

TL;DR

Implementing a new hook event UserPromptCancel or extending the existing Stop event to fire on cancellation via Ctrl+C can resolve the issue of stuck hook-driven state after a user cancels a prompt.

Guidance

  • Identify the specific hook-driven state machines that are affected by the cancellation issue and prepare to update them to handle the new UserPromptCancel event or the extended Stop event.
  • Consider the trade-offs between implementing the UserPromptCancel event, which allows for distinguishing between cancellation and natural completion, and extending the Stop event, which might be simpler but less flexible.
  • Evaluate the feasibility of the proposed workaround involving a reconciliation loop that detects and resets stale "working" states, considering its potential impact on performance and user experience.
  • Assess the need for additional events like UserPromptStart to further refine the hook event model and improve the accuracy of hook-driven state machines.

Example

# Example of how a hook-driven state machine might handle the new UserPromptCancel event
def on_user_prompt_cancel():
    # Reset the "working" indicator
    set_working_indicator(False)

# Example of how the existing Stop event might be extended to handle cancellation
def on_stop():
    # Check if the stop event was triggered by cancellation
    if was_cancelled():
        # Handle cancellation specifically
        on_user_prompt_cancel()
    # Otherwise, handle natural completion
    else:
        # ...

Notes

The implementation of the UserPromptCancel event or the extension of the Stop event requires careful consideration of the existing hook event model and its potential impact on dependent systems. The workaround involving a reconciliation loop should be used cautiously due to its potential performance implications.

Recommendation

Apply the workaround involving a reconciliation loop as a temporary solution until the UserPromptCancel event or the extended Stop event can be implemented, as it provides a more robust and accurate way to handle cancellation and maintain correct hook-driven state.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING