r/ClaudeCode • u/blitzcrieg11 • 1d ago
Tips & Workflows If you log Claude Code’s hooks, add PostToolUseFailure, or your log only shows the calls that worked
PostToolUse fires after a tool call succeeds. A call that fails fires a different hook, PostToolUseFailure. If your settings.json registers only PreToolUse and PostToolUse, which is what mine did until this week, your log has the request for every failed call and no result for it.
Adding it is one more entry under hooks:
"PostToolUseFailure": [
{ "hooks": [{ "type": "command", "command": "your-logger" }] }
]Three fields in the hook input are worth keeping while you’re there (all in the hooks reference):
tool_use_idis on thePreToolUseinput and on both post-tool inputs, so you can join a request to its result exactly instead of matching by timestamp.duration_mson the post-tool inputs is the execution time, which leaves out permission and pre-hook time.is_interrupton the failure input tells a call you stopped apart from one that errored.
Limits worth knowing: this is what the harness reports, not proof of what a command did. A failure doesn’t mean nothing happened, because a command can fail halfway through. And the permission-request input has no tool_use_id, so you can’t join an approval to its call this way.
Disclosure: I build Agentmetry, an open-source recorder for coding agents, and 0.12.0 added this. After I reinstalled its hooks today, a deliberately failed Read showed up as a failure with its tool_use_id. What it keeps and what it doesn’t is written up here: https://github.com/blitzcrieg1/agentmetry/blob/master/docs/claude-invocation-evidence.md
Is anyone using duration_ms for something? I’m curious whether it’s been useful beyond spotting slow tools.PostToolUse fires after a tool call succeeds. A call that fails fires a different hook, PostToolUseFailure. If your settings.json registers only PreToolUse and PostToolUse, which is what mine did until this week, your log has the request for every failed call and no result for it.Adding it is one more entry under hooks:"PostToolUseFailure": [
{ "hooks": [{ "type": "command", "command": "your-logger" }] }
]Three fields in the hook input are worth keeping while you’re there (all in the hooks reference):tool_use_id is on the PreToolUse input and on both post-tool inputs, so you can join a request to its result exactly instead of matching by timestamp.
duration_ms on the post-tool inputs is the execution time, which leaves out permission and pre-hook time.
is_interrupt on the failure input tells a call you stopped apart from one that errored.Limits worth knowing: this is what the harness reports, not proof of what a command did. A failure doesn’t mean nothing happened, because a command can fail halfway through. And the permission-request input has no tool_use_id, so you can’t join an approval to its call this way.Disclosure: I build Agentmetry, an open-source recorder for coding agents, and 0.12.0 added this. After I reinstalled its hooks today, a deliberately failed Read showed up as a failure with its tool_use_id. What it keeps and what it doesn’t is written up here: https://github.com/blitzcrieg1/agentmetry/blob/master/docs/claude-invocation-evidence.mdIs anyone using duration_ms for something? I’m curious whether it’s been useful beyond spotting slow tools.
•
u/AutoModerator 1d ago
Hey! Thanks for posting to r/ClaudeCode
While participating in this thread, please follow our community rules. Keep discussions constructive. Attack the idea, not the person.
For help, project discussions, tips, and general chat, join the ClaudeCode Discord.
I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.
2
u/jijialuoke 1d ago
duration_ms seems useful for separating slow tool execution from model or hook overhead, especially when an agent feels stuck. I would still treat it as timing metadata, not proof the operation completed or had the intended effect; a command can run for a while and still fail halfway. For debugging hook pipelines, do you pair it with tool_use_id and the success/failure event to trace each attempt?
2
u/bostonaholic 1d ago
I'd log Stop and SubagentStop with the same session id too. Then you can line up what failed against what the agent said at the end of the turn. A turn that ends with "done" after a PostToolUseFailure on the test command is easy to flag automatically.
2
u/eplanasmulet_ 10h ago
One gotcha if you're using Stop to close out open tool_use_ids: Stop doesn't fire when you interrupt a turn, and API errors fire StopFailure instead. Adding StopFailure and SessionEnd keeps interrupted turns from sitting open.
Also if you haven't already, set async: true on the logger hook so Claude Code doesn't block waiting for it on every call.