Repository navigation
Fixed PreToolUse and Stop hook HTTP 502 errors (regression in claude-code 2.1.118) - #86
Merged
Merged
Conversation
…code 2.1.118) Two root causes: 1. Hook servlet blocked a Jetty thread per pending request (user had not yet clicked Accept/Reject in the diff dialog). When multiple diffs were open simultaneously the thread pool was exhausted and subsequent requests returned 502. Fixed by converting HookServlet to async Jetty API (startAsync / whenComplete) so threads are released immediately after reading the request. Added MCPSseServerPerSessionTest TC-5/TC-6 and test_hook_concurrent.py to reproduce and guard against regression. 2. Hook and MCP SSE URLs used "localhost" which on systems where localhost resolves to ::1 (IPv6) first causes claude-code's Bun HTTP client to connect to ::1:PORT. Jetty's ServerConnector binds to 0.0.0.0 (IPv4 only) and refuses the connection; claude-code reports the failure as "HTTP 502". Fixed by using 127.0.0.1 instead of localhost in all hook and SSE URLs written to settings.local.json. Added test_hook_ipv6_localhost.py to verify. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
oleg68
added a commit
that referenced
this pull request
Apr 24, 2026
…code 2.1.118) (#87) Backport of #86 to release/0.21. ## Summary Two root causes for HTTP 502 on PreToolUse/Stop hooks: - **Thread exhaustion**: `HookServlet` blocked a Jetty thread per pending request. With multiple diff dialogs open simultaneously the thread pool was exhausted. Fixed by converting `HookServlet` to async Jetty API (`startAsync` / `whenComplete`). - **IPv6 vs IPv4**: Hook and MCP SSE URLs used `localhost` which on systems where `localhost` resolves to `::1` (IPv6) first causes claude-code's Bun HTTP client to connect to `::1:PORT`. Jetty's `ServerConnector` binds to `0.0.0.0` (IPv4 only) and refuses the connection; claude-code reports this as "HTTP 502". Fixed by using `127.0.0.1` instead of `localhost` in all URLs written to `settings.local.json`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Oleg Samarin <osamarin@openintegration.inc> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two root causes for HTTP 502 on PreToolUse/Stop hooks:
HookServletblocked a Jetty thread per pending request. With multiple diff dialogs open simultaneously the thread pool was exhausted. Fixed by convertingHookServletto async Jetty API (startAsync/whenComplete).localhostwhich on systems wherelocalhostresolves to::1(IPv6) first causes claude-code's Bun HTTP client to connect to::1:PORT. Jetty'sServerConnectorbinds to0.0.0.0(IPv4 only) and refuses the connection; claude-code reports this as "HTTP 502". Fixed by using127.0.0.1instead oflocalhostin all URLs written tosettings.local.json.Test plan
MCPSseServerPerSessionTestTC-5 and TC-6: verify async servlet does not block threads and N concurrent hook requests all return 200claude-launch-tests/test_hook_concurrent.py: starts claude, asks it to write multiple files simultaneously, verifies no 502 in PTY outputclaude-launch-tests/test_hook_ipv6_localhost.py: starts an IPv4-only HTTP server, verifieslocalhostURL causes 502 while127.0.0.1URL works correctly🤖 Generated with Claude Code