SessionStart hooks fire twice on /resume, clobbering first invocation's output

Resolved 💬 4 comments Opened Mar 4, 2026 by kitaekatt Closed Mar 8, 2026

Bug

When resuming a session with /resume, Claude Code fires SessionStart hooks twice — once for the resume event, once for the regular SessionStart. This causes the second invocation's output to overwrite the first invocation's user-visible systemMessage.

Reproduction

  1. Create a SessionStart hook that does meaningful work on first run and caches the result
  2. On subsequent runs (cache hit), emit a short "ok (cached)" message with suppressOutput: false
  3. Start a session (hook runs, does real work, shows detailed output)
  4. Use /resume to resume the session
  5. Observe: the detailed first-run output is clobbered by the cached "ok" message from the second invocation

Expected behavior

SessionStart hooks should fire once per /resume, not twice. The hook receives a source field distinguishing "startup" from "resume", so firing it twice (presumably once with each source) is redundant — the hook can branch internally on that field.

Impact

  • Wasted computation: Hooks that do real work (venv setup, dependency fetching, config detection) run twice unnecessarily
  • Suppressed output: The user never sees what the bootstrap actually did, because the second (cached) invocation's output replaces it
  • Workaround required: Plugin authors must make cached responses silent (suppressOutput: true) to avoid clobbering, which means users lose visibility into cache-hit status on normal startups too

Workaround

Make the cached path always emit suppressOutput: true so the second invocation doesn't overwrite the first. This works but is a workaround — the root cause is the double invocation.

View original on GitHub ↗

This issue has 4 comments on GitHub. Read the full discussion on GitHub ↗