SessionStart hooks not working for new conversations
Bug Report: SessionStart Hooks Not Working for New Conversations
Environment
- Claude Code Version: 2.0.27
- Platform: macOS (Darwin 24.6.0)
- Node.js Version: v23.11.0
- Installation Type: npm-global (via Homebrew)
Summary
SessionStart hooks execute but their output is never processed or injected into Claude's context when starting new conversations. The hooks work correctly for /clear, /compact, and URL resume operations, but fail silently for brand new sessions.
Expected Behavior
When a user starts a new Claude Code conversation, SessionStart hooks should:
- Execute successfully
- Have their stdout parsed for
hookSpecificOutput.additionalContext - Create
hook_additional_contextattachments - Inject that context into Claude's conversation
According to the documentation, "For UserPromptSubmit and SessionStart, stdout is added as context."
Actual Behavior
- SessionStart hooks do execute (verified by file logging)
- Hook stdout is never processed by the
qz()function - The
hook_additional_contextattachments are never created - Context is not injected into new conversations
- No error messages are shown to the user
Root Cause Analysis
Code Investigation
The qz() function in cli.js is responsible for:
- Executing SessionStart hooks
- Collecting their stdout
- Parsing JSON output for
hookSpecificOutput.additionalContext - Creating
hook_additional_contextattachments - Returning those attachments for injection into the conversation
Problem: qz() is only called in 3 places in the entire codebase:
- Line 1798:
await qz("compact")- during/compactcommand ✅ - Line 3723:
await qz("startup")- during URL resume (--resumewith URL) ✅ - Line 3726:
await qz("startup")- during/clearcommand ✅
For brand new interactive conversations, the wm6() function (line 3717) only replays old hook responses from message history:
let H=FZ2(G),w=[],N=!1;
for(let g of H)if(g.type==="system"&&g.subtype==="hook_response"&&g.hook_event==="SessionStart")w.push(g);
It never calls qz("startup") for new sessions, so SessionStart hooks execute but their output is discarded.
Verification Steps Taken
- Confirmed hooks execute: Added logging to SessionStart hook - timestamps written to
/tmp/claude-session-start.logproved hooks ran - Confirmed no output injection: Created test hook outputting secret word - Claude had no knowledge of it in new sessions
- Added debug logging: Instrumented
qz(),Gf2(), and hook processing functions - confirmedqz()never called for new sessions - Source code review: Examined
cli.js- found only 3 call sites forqz(), none for new interactive sessions
Reproduction Steps
1. Create a test SessionStart hook
#!/bin/bash
cat <<'EOF'
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "The secret word is \"bamboozle\"."
}
}
EOF
exit 0
2. Configure in .claude/settings.local.json
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "/absolute/path/to/test_hook.sh"
}
]
}
]
}
}
3. Start a brand new conversation
claude
4. Ask Claude
> what is the secret word?
Expected: Claude responds "bamboozle"
Actual: Claude has no knowledge of any secret word
5. Verify hook executed
cat /tmp/hook-executed.log # If hook logs timestamps
Hook timestamps confirm execution, but context was not injected.
The Fix
Add qz("startup") call inside the wm6 function's async helper where hooks are processed:
File: /opt/homebrew/lib/node_modules/@anthropic-ai/claude-code/cli.js
Location: Line ~3717, inside the _=async()=>{} function
Original code:
_=async()=>{
if(V=!0,!N){
N=!0;
for(let c of w)E.enqueue(c)
}
// ... rest of function
Fixed code:
_=async()=>{
if(V=!0,!N){
N=!0;
if(w.length===0&&H.length===0){
let s=await qz("startup");
w.push(...s);
}
for(let c of w)E.enqueue(c)
}
// ... rest of function
Explanation: This checks if there are no hooks from history (w.length===0) and no messages (H.length===0), indicating a new session, then calls qz("startup") to process SessionStart hooks before enqueuing them.
Verification After Fix
After applying the fix:
- Started new conversation
- SessionStart hook output appeared:
````
SessionStart:startup hook succeeded: The secret word is "bamboozle".
- Asked Claude for secret word
- Claude correctly responded with "bamboozle"
Impact
This bug affects all users who configure SessionStart hooks for:
- Injecting project-specific rules/context
- Setting up session-specific configuration
- Loading workspace information
Workaround: Users must manually run /clear at the start of each session to trigger SessionStart hooks, which is not intuitive.
Additional Notes
- PreToolUse and PostToolUse hooks work correctly
- UserPromptSubmit hooks work correctly
- The UI correctly hides
hook_additional_contextattachments (line 1818 incli.js) - The bug only affects the interactive CLI mode, not
--printmode (which callsqz("startup")at line 3726)
Files for Reference
- Main implementation:
cli.jsline 3717 (wm6function) - Hook processing:
cli.jsline ~1798 (qzfunction) - Context extraction:
cli.js(Gf2function) - UI rendering:
cli.jsline 1818 (hides hook_additional_context)
Proposed Solution
Apply the fix shown above, or alternatively refactor to ensure qz("startup") is called for all new sessions, not just URL resumes and command-triggered operations.
20 Comments
Found 3 possible duplicate issues:
This issue will be automatically closed as a duplicate in 3 days.
🤖 Generated with Claude Code
Not a duplicate:
Confirmed this issue. Setup:
Result: On fresh session start (via /new command), no hook output appears in context. Hook appears to execute silently or output is discarded.
Workaround attempted: None successful without manually running the script.
Config:
This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.
how is this still open ... this is in your docs...
+1 it doesn't work
+1 same here
Title: SessionStart hooks don't fire reliably when opening new VSCode window with multiple windows already open
Description:
When multiple VSCode windows are running Claude Code and I open a new window, plugins that rely on SessionStart hooks fail to load properly. Commands appear in the slash menu but return "Unknown skill" when invoked.
Other plugins (beads, pr-review-toolkit) that register skills statically work fine
~/.claude/plugins/cache/temp_git_* directories accumulate with .orphaned_at markers (225+ in my case)
Running /clear fixes it by re-triggering SessionStart
Reinstalling the plugin also fixes it temporarily
Affected plugin: superpowers (but likely affects any plugin using SessionStart hooks)
Confirmed same issue. Claude Code led me here in troubleshooting it.
I can also confirm that this is broken for me. (Startup hook is ignored.)
Also just tried to create a session startup hook. Same problem. Please fix.
Facing the same issue.
Also experiencing this. As a workaround, I tried moving context injection to
UserPromptSubmithooks instead ofSessionStart, but that has its own issues - theadditionalContextfromUserPromptSubmithooks also doesn't get injected into Claude's context (related to #12151).So currently there's no reliable way to inject dynamic context at either:
This makes it impossible to build automation that feeds external information into Claude conversations. Would really appreciate a fix for the hook → context injection pipeline.
Facing the same issue
Wonderful, another broken configuration. I'm here after trying to write a hook to work around user permissions being ignored to copy user permissions to project permissions. OF COURSE that doesn't work either. Why host a github repo for something if you're going to ignore fundamental issues people are filing?
Reporting empirical findings from Claude Code VSCode extension (March 2026):
startupmatcher is now firing correctly in VSCode — both on Reload and on/newcommand.Observed behavior (session-start-context-load log, 2026-03-03):
/newcommand: single fire observed (1 data point; pattern vs Reload needs further observation)This suggests the root cause described in this issue has been resolved in recent versions. Environment: Windows 11, VSCode extension, Claude Code v2.1.6x.
This was working for me until very recently and now seems to have stopped.
Opus is already a bit flakey about following instruction reliably so these hooks are the only way to inject a bit of determinism.
Sad state of affairs.
Experiencing this on v2.1.19 (Linux).
/clearThe intermittent nature is puzzling - no identified variable changed between working and non-working state.
Also experiencing this intermittently on macOS (CLI, not VSCode). I built Tacklebox, a hook server for session coordination and context persistence, and ran into this early on. My workaround is a dual-path injection: SessionStart builds and returns context normally, but for startup sessions we deliberately skip setting an internal context_injected flag. This lets UserPromptSubmit on the first prompt act as a fallback, if SessionStart was silently discarded, UserPromptSubmit catches it and injects the same context. Either way, context is delivered reliably.
Noting that TanakaSeiji's findings above match what we've seen: resume, compact, and clear sources work consistently; it's only startup that's unreliable. The behavior seems environment-dependent and can come and go between versions without any config changes on our side.
When Claude refers to "the issue" it means this issue.
This is annoying, still happening, please fix.