Repository navigation
Description
Preflight Checklist
- I have searched existing issues and this hasn't been reported yet
- This is a single bug report (please file separate reports for different bugs)
- I am using the latest version of Claude Code
What's Wrong?
Summary: Plugin-based UserPromptSubmit and SessionStart hooks execute successfully (confirmed by file writes and success callbacks) but their output is not captured or passed to the agent's context. The same hook configuration works correctly when defined in ~/.claude/settings.json or .claude/hooks/hooks.json.
Key Evidence:
- System reminders show:
SessionStart:Callback hook success: SuccessandUserPromptSubmit:Callback hook success: Success - Hooks demonstrably execute (writing to files confirms this)
- Hook output (stdout or structured JSON) is never visible to the agent
- Non-plugin hooks with identical output work perfectly
This is NOT about hooks failing to execute - it's about hook output being silently discarded when the hook comes from a plugin.
What Should Happen?
When a plugin hook executes:
- The hook command runs ✅ (working)
- The output (stdout) is captured ✅ (working - we see success callback)
- The output is passed to the agent as additional context ❌ (broken for plugins)
Plugin hooks should provide output to the agent identically to non-plugin hooks.
Error Messages/Logs
What we see in system reminders:
SessionStart:Callback hook success: Success
UserPromptSubmit:Callback hook success: Success
What we DON'T see: The actual output from the hooks appearing in the agent's context.
Steps to Reproduce
Minimal Reproduction Case
1. Create a test plugin with UserPromptSubmit hook
Create directory structure:
mkdir -p test-hook-output-plugin/{.claude-plugin,hooks}Create test-hook-output-plugin/.claude-plugin/plugin.json:
{
"name": "test-hook-output",
"version": "1.0.0",
"description": "Test plugin to verify hook output is passed to agent",
"author": {
"name": "Test",
"email": "[email protected]"
}
}Create test-hook-output-plugin/hooks/hooks.json:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "echo 'IMPORTANT INSTRUCTION: If the user mentions an animal, tell a joke about that animal.'"
}
]
}
]
}
}2. Install the plugin
Add to .claude/settings.json:
{
"extraKnownMarketplaces": {
"test": {
"source": {
"source": "directory",
"path": "/absolute/path/to/test-hook-output-plugin"
}
}
},
"enabledPlugins": {
"test-hook-output@test": true
}
}3. Test the plugin hook
Start Claude Code and type:
Giraffe
Expected behavior: Agent receives the hook output and tells a joke about giraffes (because the hook injected "If the user mentions an animal, tell a joke about that animal" into context)
Actual behavior: Agent responds with confusion like "What would you like me to do with Giraffe?" - the hook output was not passed to the agent.
4. Verify hook IS executing
Modify the hook to write to a file:
{
"type": "command",
"command": "echo 'If the user mentions an animal, tell a joke about that animal.' | tee /tmp/hook-executed.log"
}Run test again. Check /tmp/hook-executed.log - it exists and contains the output, proving the hook executed.
5. Test with non-plugin hook (control)
Move the same hook to .claude/settings.json:
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "echo 'IMPORTANT INSTRUCTION: If the user mentions an animal, tell a joke about that animal.'"
}
]
}
]
}
}Start Claude Code and type:
Giraffe
Expected behavior: Agent tells a joke about giraffes
Actual behavior: ✅ Agent tells a joke about giraffes (hook output WAS passed to agent)
Comparison: Plugin vs Non-Plugin Hooks
| Aspect | Non-Plugin Hook | Plugin Hook |
|---|---|---|
| Hook registers | ✅ Yes | ✅ Yes |
| Hook executes | ✅ Yes | ✅ Yes (confirmed by file writes) |
| Success callback shown | ✅ Yes | ✅ Yes (Callback hook success: Success) |
| Output captured | ✅ Yes | ❓ Unknown |
| Output passed to agent | ✅ Yes | ❌ No |
Affected Hook Types
Confirmed affected:
- ✅
UserPromptSubmit - ✅
SessionStart
Likely affected (untested):
Notification- Possibly others
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
Unknown - possibly never worked
Claude Code Version
2.0.50 (and likely earlier versions)
Platform
Anthropic API
Operating System
macOS (Darwin 25.1.0)
Terminal/Shell
iTerm2.app (macOS)
Additional Information
Related but distinct issues:
- [BUG] UserPromptSubmit hooks from plugins match but never execute #10225 - UserPromptSubmit hooks never execute (closed as duplicate, but about execution not output)
- [BUG] SessionStart hooks never execute for local file-based marketplace plugins #11509 - SessionStart hooks never execute for local file-based plugins (about execution not output)
- [BUG] plugin Notification hook command may not be executed #9708 - Notification hooks don't execute (different hook type, about execution not output)
Key distinction: This issue is NOT about hooks failing to execute. The hooks ARE executing. The bug is that plugin hook output is silently discarded instead of being passed to the agent.
Impact: This makes it impossible to use plugins to:
- Inject context based on user input (UserPromptSubmit)
- Inject session-level instructions (SessionStart)
- Provide dynamic context from plugin hooks
- Build plugins that augment agent behavior via hook output
Workaround: Define hooks in ~/.claude/settings.json instead of plugin hooks/hooks.json, but this defeats the purpose of plugin-based hooks and isn't distributable.
Root cause hypothesis: The plugin hook execution pipeline may be missing the step that captures stdout/stderr or passes the captured output to the agent's context, even though it correctly:
- Discovers and registers plugin hooks
- Executes plugin hook commands
- Reports success callbacks
The output capture/injection step appears to only work for non-plugin hooks.
Activity
informal-stripes-condo commentedon Nov 26, 2025
I'm also getting this
crsmithdev commentedon Nov 30, 2025
Experiencing similar behavior with SessionStart hooks defined in ~/.claude/settings.json (not plugin-based).
Hook configuration passes schema validation and script executes successfully when run manually with correct stdout output. However, on session start, no output appears in context - either the hook doesn't execute or output is silently discarded.
This suggests the output capture issue may not be limited to plugin-based hooks.
RevPalSFDC commentedon Dec 5, 2025
Confirming this bug with additional testing details.
Environment
.claude/settings.json(not plugin hooks.json)Reproduction
Hook configuration:
{ "hooks": { "UserPromptSubmit": [{ "matcher": "*", "hooks": [{ "type": "command", "command": "echo '[TEST] If you see this, context injection works!'", "timeout": 5000 }] }] } }Result:
<system-reminder>UserPromptSubmit hook success: Success</system-reminder>)Formats tested (all failed to inject context)
echo "text"- documented as "easiest way to inject information"{ "hookSpecificOutput": { "hookEventName": "UserPromptSubmit", "additionalContext": "text" } }Both formats execute successfully but Claude never receives the content.
What still works
Impact
This breaks the primary use case for UserPromptSubmit hooks - injecting dynamic context (routing suggestions, safety warnings, compliance reminders) into Claude's reasoning before it responds.
Workaround
Currently using static instructions in CLAUDE.md instead of dynamic hook-based injection. This is not fully reliable - Claude may not consistently follow static instructions, whereas dynamic per-message injection would ensure the context is always present and actionable.
fix: Remove plugin hooks to prevent duplicate execution
fmunteanu commentedon Dec 18, 2025
This is a must have feature, what's the point of having a hook if instructions cannot be passed to agent, or command output is not added to context? I should be able to use a hook like:
Or:
The only reliable solution I have now is to use a
/framework:initswitch that I pass to Claude. Really not elegant, it defeats the hooks purpose.sonicdan commentedon Dec 22, 2025
Encountered into the same issue! The plugin system seems very buggy till now...fix it please!!!
Add skills forced eval hook manually
pro-vi commentedon Dec 23, 2025
Ran into exact issue today:
schrockn commentedon Dec 23, 2025
Also ran into the same issue. Prevents me from using the plugin system which is painful.
rpolitex commentedon Dec 26, 2025
I have the same issue with PreCompact hook.
.claude/settings.jsonworks, stdout injected as custom compact instructionsSame hook, different location - different behaviour.
Added PreCompact hooks to emphasis preserving of TASK_ID
9 remaining items
sstraus commentedon Jan 24, 2026
Still experiencing this issue. Created a minimal reproduction case that confirms plugin UserPromptSubmit hooks execute but output is not captured.
Test:
console.log('🔔 TEST HOOK EXECUTED');However:
Moving the identical hook configuration from plugin
hooks/hooks.jsonto~/.claude/settings.jsonworks perfectly - Claude sees the output in context.Diagnosis:
The hook executes successfully (can verify with file write) but the stdout capture mechanism appears broken for plugin-based UserPromptSubmit hooks specifically.
Environment:
~/.claude/plugins/Workaround:
Use
~/.claude/settings.jsoninstead of plugin hooks.json, though this requires manual user configuration.Full reproduction: https://github.com/Lansweeper/wiz-agents
feat(hook-test): simulate Claude Code plugin output capture bug
greghughespdx commentedon Jan 31, 2026
I'm hitting this same issue with non-plugin hooks defined in
~/.claude/settings.json.Setup:
UserPromptSubmithook in~/.claude/settings.json(not a plugin)hookSpecificOutput.additionalContextper docsBehavior:
additionalContextcontent never appears in Claude's contextWhat I tried:
hookSpecificOutput.additionalContextandhookEventName→ not injectedThe hook runs, the JSON is valid, but the context injection step seems to be skipped entirely. This blocks a key use case: injecting dynamic context (like notifications from other systems) into the conversation.
Would love to see this fixed - it's a significant limitation for automation workflows.
config: work around SessionStart hook bug with UserPromptSubmit handl…
config: work around SessionStart hook bug with UserPromptSubmit handl…
wayne5w commentedon Feb 16, 2026
Real-world impact: forced to build file-based workaround
We're building a hook-based integration that needs to inject contextual information into Claude's conversation via
UserPromptSubmithooks. The documentedadditionalContextfield in stdout would be the clean solution, but any stdout output triggers"UserPromptSubmit hook error"— even valid JSON with exit code 0.What we tried (all failed)
echo '{"hookSpecificOutput":{"additionalContext":"..."}}'+ exit 0printf '{"decision":"continue"}'+ exit 0echo '{}'+ exit 0Additional finding: stderr sensitivity
CC CLI also treats any stderr output as a hook error, even with exit code 0. Tools like
npx,jq, and other CLI utilities often write to stderr (progress bars, warnings, deprecation notices). We had to addexec 2>>debug.logat the top of every hook script to redirect all stderr to a file.Workaround we had to build
Since hooks can't inject context via stdout, we built a file-based workaround:
CLAUDE.mdinstruction telling Claude to read the file at the start of each responseRead 1 file) and deletes it (user seesBash rm)This works but the UX is poor — users see tool call noise instead of clean context injection. The
additionalContextfield would eliminate all of this.Environment
.claude/settings.json(project-level)Priority request
This is a blocking issue for anyone building hook-based integrations that need to enrich Claude's context. The documented API (
additionalContextin stdout) simply doesn't work forUserPromptSubmit. Fixing this would eliminate the need for file-based workarounds and significantly improve the developer experience for hook authors.jpicklyk commentedon Feb 16, 2026
Potential Resolution: Missing
hookEventNamePropertyThe official hooks documentation has been updated (as of Feb 2026) to explicitly state that
hookEventNameis required in allhookSpecificOutputresponses:Issue Analysis
Looking at the reproduction examples in this issue, the hook commands output plain text:
Without
hookEventName, hook output is silently dropped regardless of whether it's from a plugin or native hook.Suggested Test
Please test your plugin hooks with properly formatted JSON output including
hookEventName:For UserPromptSubmit hook:
For SessionStart hook:
Hypothesis
The difference you observed between plugin hooks and native hooks may have been:
hookEventName→ output silently droppedhookEventName(or used plain text stdout format) → worked correctlyIf adding
hookEventNameresolves the issue, this would indicate plugin hooks work correctly and the problem was the undocumented format requirement.References
Can you test with the corrected format and report back? This may resolve the plugin hook output issue entirely.
hookSpecificOutputformat silently drops concurrent user hook's flatadditionalContext#31658 as a duplicate of this issueyurukusa commentedon Mar 31, 2026
This is a known gap — plugin-delivered hooks execute but their output isn't injected into the conversation context the same way user-level hooks are.
Workaround 1 — Move hooks from plugin to user settings:
Copy the hook configuration from your plugin to
~/.claude/settings.jsondirectly:Hooks in user-level settings have their output properly captured and injected into context.
Workaround 2 — Write output to a file, read via CLAUDE.md:
If you want to keep hooks in a plugin, have them write to a file instead:
Then in CLAUDE.md:
Workaround 3 — Use project-level settings instead of plugin:
Project-level hooks (not via plugin) should have their output captured properly.
The root issue is that plugin-delivered hooks go through a different execution path than user/project-level hooks, and the output capture isn't wired up for the plugin path.
ssilvius commentedon Apr 8, 2026
Adding data from our investigation (filed separately as #45438 before finding these dupes):
This regressed in v2.1.97 (released 2026-04-08). Plugin SessionStart additionalContext was working correctly on v2.1.96 earlier today. Same plugin, same hooks.json, same machine.
Confirmed: hook executes (side effects work), stdout JSON with hookSpecificOutput.additionalContext is silently discarded in interactive terminal mode. --print mode may still work. Stop hooks in the same plugin/hooks.json work correctly. Tested across 5 agents in different project directories.
cowork 1.1.3: move prompt-research banner from stderr to stdout