From d93aee5137b956a279f5c473e5be0744dcfabd77 Mon Sep 17 00:00:00 2001 From: ShuntaH Date: Wed, 22 Jul 2026 14:20:48 +0900 Subject: [PATCH] Fix SessionEnd hook cancelled on exit by raising its 1s timeout The injected HOOKS_JSON gave the SessionEnd hook a 1s timeout while every sibling synchronous hook uses 5-125s. On a normal session exit, cold-starting the cmux binary to run `cmux hooks claude session-end` routinely exceeds 1s, so Claude Code cancels it and prints: SessionEnd hook [...] failed: Hook cancelled leaving the session-end cleanup unrun. The hook itself is fine (invoking it manually returns OK instantly) -- only the 1s ceiling was too tight. Raise the SessionEnd timeout to 10s to match the other synchronous hooks. Kept synchronous rather than async so cleanup completes before teardown. Fixes #8637 --- Resources/bin/cmux-claude-wrapper | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Resources/bin/cmux-claude-wrapper b/Resources/bin/cmux-claude-wrapper index 1cd5d09487bd..419544a576d0 100755 --- a/Resources/bin/cmux-claude-wrapper +++ b/Resources/bin/cmux-claude-wrapper @@ -885,7 +885,11 @@ install_cmux_node_options # - preferredNotifChannel: disables Claude Code's OSC/terminal notification # channel inside cmux so hooks are the only notification source. # - SessionStart/Stop/Notification: existing lifecycle hooks -# - SessionEnd: cleanup when Claude exits (covers Ctrl+C where Stop doesn't fire) +# - SessionEnd: cleanup when Claude exits (covers Ctrl+C where Stop doesn't fire). +# Synchronous (not async) so cleanup completes before Claude tears down, with +# the same 10s budget as the other synchronous hooks. A 1s timeout was too +# tight: cold-starting the cmux binary at teardown routinely exceeds it, so +# Claude Code cancels the hook ("Hook cancelled") before cleanup runs (#8637). # - UserPromptSubmit: clears "Needs input" and sets "Running" on new prompt # - PreToolUse: rejects unsupported durable cron requests synchronously, then # (async) clears "Needs input" / sets "Running" for ordinary tools, and flags @@ -912,7 +916,7 @@ install_cmux_node_options # timeout because it may run a summarization subprocess; it gates itself # on the workspaceAutoNaming setting via a socket probe, so it is a # no-op when the feature is disabled. -HOOKS_JSON='{"preferredNotifChannel":"notifications_disabled","hooks":{"SessionStart":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude session-start","timeout":10}]}],"Stop":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude stop","timeout":10}]},{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks feed --source claude","timeout":10,"async":true}]},{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude auto-name","timeout":120,"async":true}]}],"SubagentStop":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks feed --source claude","timeout":10,"async":true}]}],"SessionEnd":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude session-end","timeout":1}]}],"Notification":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude notification","timeout":10}]}],"UserPromptSubmit":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude prompt-submit","timeout":10}]}],"PreToolUse":[{"matcher":"CronCreate","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude cron-create-guard","timeout":5}]},{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude pre-tool-use","timeout":5,"async":true}]}],"PostToolUse":[{"matcher":"PushNotification","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude push-notification","timeout":10,"async":true}]}],"PermissionRequest":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks feed --source claude","timeout":125}]}]}}' +HOOKS_JSON='{"preferredNotifChannel":"notifications_disabled","hooks":{"SessionStart":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude session-start","timeout":10}]}],"Stop":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude stop","timeout":10}]},{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks feed --source claude","timeout":10,"async":true}]},{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude auto-name","timeout":120,"async":true}]}],"SubagentStop":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks feed --source claude","timeout":10,"async":true}]}],"SessionEnd":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude session-end","timeout":10}]}],"Notification":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude notification","timeout":10}]}],"UserPromptSubmit":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude prompt-submit","timeout":10}]}],"PreToolUse":[{"matcher":"CronCreate","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude cron-create-guard","timeout":5}]},{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude pre-tool-use","timeout":5,"async":true}]}],"PostToolUse":[{"matcher":"PushNotification","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks claude push-notification","timeout":10,"async":true}]}],"PermissionRequest":[{"matcher":"","hooks":[{"type":"command","command":"\"${CMUX_CLAUDE_HOOK_CMUX_BIN:-cmux}\" hooks feed --source claude","timeout":125}]}]}}' # Fold any user-provided --settings into HOOKS_JSON before launching. #