Description
When a hook writes to stderr (e.g., exit 2 for DENY/block), Claude Code displays the message with the full command as a prefix:
Stop hook error: [node /path/to/hook.mjs stop]: Custom message here.
The [node /path/to/hook.mjs stop] prefix is an implementation detail that provides no value to users. For plugin hooks, it exposes internal paths like ${CLAUDE_PLUGIN_ROOT}/dist/hook.mjs.
Expected behavior
Show only the hook's stderr message, optionally with a human-readable hook identifier:
Stop hook: Custom message here.
Or use the plugin name if available:
my-plugin (Stop): Custom message here.
Reproduction
- Create a minimal hook that exits with code 2:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "echo 'Please fix errors before finishing.' >&2 && exit 2",
"timeout": 5
}
]
}
]
}
}
- Start a session and let Claude attempt to stop
- Observe the output:
Stop hook error: [echo 'Please fix errors before finishing.' >&2 && exit 2]: Please fix errors before finishing.
The full command is shown as a prefix, and the intentional block is labeled "error".
Additional context
Description
When a hook writes to stderr (e.g., exit 2 for DENY/block), Claude Code displays the message with the full command as a prefix:
The
[node /path/to/hook.mjs stop]prefix is an implementation detail that provides no value to users. For plugin hooks, it exposes internal paths like${CLAUDE_PLUGIN_ROOT}/dist/hook.mjs.Expected behavior
Show only the hook's stderr message, optionally with a human-readable hook identifier:
Or use the plugin name if available:
Reproduction
{ "hooks": { "Stop": [ { "hooks": [ { "type": "command", "command": "echo 'Please fix errors before finishing.' >&2 && exit 2", "timeout": 5 } ] } ] } }The full command is shown as a prefix, and the intentional block is labeled "error".
Additional context