[BUG] `"async": true` is silently ignored on `type: "http"` hooks — the turn still waits for the response
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
"async": true is silently ignored on type: "http" hooks. Claude Code still waits for the HTTP response before it continues, exactly as it does for a sync http hook. The request does reach the server, so the hook runs; it just blocks.
With a command hook, "async": true runs the hook in the background and the turn doesn't wait. For http hooks no warning is shown and the field has no effect. Since the documentation describes async only for command hooks, this may be a missing feature rather than a bug. Either way, the setting is accepted without effect.
This matters for tools that report session state to a local server on every tool call: an http hook on PreToolUse/PostToolUse adds the server's response time, plus some fixed per-hook overhead, to every tool turn. A background command hook doesn't.
What Should Happen?
One of:
"async": trueon an http hook is fire-and-forget, like on a command hook: the request is sent and the turn continues without waiting for the response (the response is ignored, as for async command hooks).- Or settings validation warns that
asyncisn't supported ontype: "http", and the docs say so.
Error Messages/Logs
No error. Measured over a no-hook baseline, with a local server that answers every hook after 50 ms, ~20 tool turns per variant, p50 of the time Claude spends before and after each tool call:
variant before tool after tool hook requests per turn
http (sync) +62.6 ms +57.1 ms 2.0
http with "async": true +65.1 ms +58.5 ms 2.0
Steps to Reproduce
- Run a local server that answers every request after a delay, e.g.:
``js``
// delay-server.mjs
import http from 'node:http'
http.createServer((req, res) => {
req.resume()
req.on('end', () => setTimeout(() => {
res.writeHead(200, { 'content-type': 'application/json' })
res.end('{}')
}, Number(process.env.DELAY_MS ?? 50)))
}).listen(18901, '127.0.0.1')
DELAY_MS=2000 node delay-server.mjs makes the effect easy to see by eye.
- Use these settings (e.g. via
--settings):
``json``
{
"hooks": {
"PreToolUse": [{ "hooks": [{ "type": "http", "url": "http://127.0.0.1:18901/hook", "async": true }] }],
"PostToolUse": [{ "hooks": [{ "type": "http", "url": "http://127.0.0.1:18901/hook", "async": true }] }]
}
}
- Ask Claude to run a trivial Bash command a few times (e.g.
true). - Each tool call stalls for about the server delay before and after the tool runs. Remove
"async": trueand the timing is the same. Withtype: "command"hooks and"async": true(what we use today for the same events), the turn does not wait for the hook.
Claude Model
Not sure / Multiple models
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.296 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Other
Additional Information
The measurement ran against a local mock API endpoint with a dummy key, so the model doesn't matter here; the delay is between the tool_use and tool_result around the hook calls.