SessionStart hook only fires for the first ~5 project switches per Claude Code Desktop launch
Bug: SessionStart hook only fires for the first ~5 project switches per Claude Code Desktop launch
Summary
In Claude Code Desktop on macOS, the SessionStart hook fires reliably for the first 5 project switches after launching the app, then stops firing entirely for any further project switches — until the Desktop app is restarted, at which point the count resets and the next 5 switches fire again.
This breaks any workflow that relies on SessionStart to react to project changes (in my case: opening Zed in the same project the user just switched to in Claude Code Desktop).
CwdChanged was also tested and never fires on Desktop project switches.
Environment
- Claude Code Desktop: 1.7196.0
- macOS: 26.3.1 (build 25D771280a)
- Apple Silicon
Repro
- Configure
~/.claude/settings.jsonwith aSessionStarthook that appends to a log file:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo \"$(date '+%H:%M:%S') [SessionStart] cwd=$CLAUDE_PROJECT_DIR\" >> /tmp/hook.log",
"async": true
}
]
}
]
}
}
- Launch Claude Code Desktop.
- Switch among projects in the Desktop UI more than 5 times (alternating between 2-3 projects is enough).
- Inspect
/tmp/hook.log.
Expected
SessionStart fires on every project switch.
Actual
SessionStart fires exactly 5 times per Desktop launch, then stops permanently until the app is restarted. Subsequent project switches do not fire SessionStart (and do not fire CwdChanged either).
Evidence
Real log from my machine (annotated). The Desktop was restarted between groups:
# Run 1 — 5 fires, then silence
19:05:34 cwd=/Users/carlosj
19:05:34 cwd=/Volumes/Jellyfin/yt-dlp
19:06:04 cwd=/Users/carlosj/Desarrollo/Public/rcp2_svc
19:06:16 cwd=/Volumes/Jellyfin/yt-dlp
19:06:21 cwd=/Users/carlosj/Desarrollo/Public/rcp2_svc
# … then continued switching for ~3 minutes with NO further hook fires
# Run 2 (Desktop restarted) — 5 fires again, then silence
19:09:34 [SessionStart] cwd=/Users/carlosj
19:09:35 [SessionStart] cwd=/Volumes/Jellyfin/yt-dlp
19:09:43 [SessionStart] cwd=/Users/carlosj/Desarrollo/Public/rcp2_svc
19:09:52 [SessionStart] cwd=/Volumes/Jellyfin/yt-dlp
19:10:00 [SessionStart] cwd=/Users/carlosj/Desarrollo/Public/rcp2_svc
# Run 3 (Desktop restarted) — same pattern, 5 fires then silence
19:12:43 [SessionStart] cwd=/Users/carlosj
19:12:43 [SessionStart] cwd=/Volumes/Jellyfin/yt-dlp
19:13:01 [SessionStart] cwd=/Users/carlosj/Desarrollo/Public/rcp2_svc
19:13:09 [SessionStart] cwd=/Volumes/Jellyfin/yt-dlp
19:13:18 [SessionStart] cwd=/Users/carlosj/Desarrollo/Public/rcp2_svc
# Switched many more times, no further [SessionStart].
# UserPromptSubmit (used as fallback) DOES still fire from this point onward.
Hypothesis
Claude Code Desktop appears to maintain a pool of up to ~5 pre-warmed SDK processes (visible as claude --resume <id> in ps). The first 5 unique project visits each spawn a fresh SDK process and fire SessionStart. After the pool is full, subsequent project switches route the user to a cached process without firing SessionStart (and without firing CwdChanged).
If this is by design, please consider either:
- Firing
SessionStarton cached-session reuse as well, OR - Exposing a separate event (e.g.
ProjectActivatedorSessionResumed) that fires on every project switch in the Desktop UI, regardless of whether a new SDK process spawns.
Workaround currently in use
UserPromptSubmit with deduplication (only call open -a Zed <path> when the path differs from the last) is a working fallback, but adds a lag of one prompt before the editor follows the project switch.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗