SessionStart hooks do not fire on /clear (related to #10373)

Resolved 💬 4 comments Opened Mar 13, 2026 by tpbkpkxv8x-ux Closed Apr 13, 2026

[BUG] SessionStart hooks do not fire on /clear (related to #10373)

Summary

SessionStart hooks configured in project .claude/settings.json execute correctly on startup (via --init-only and fresh sessions) but never execute when triggered by /clear. This may be related to #10373, which reports a different failure mode for the same hook type. Other hook types (e.g. PostToolUse) from the same settings file fire correctly during the same session.

Environment

  • Claude Code: 2.1.72
  • Node: v22.22.0
  • Platform: Linux aarch64 (Docker container, kernel 6.12.72-linuxkit)
  • Shell: bash

Minimal reproduction

1. Set up a test repo with SessionStart and PostToolUse hooks:

mkdir /tmp/hook-test && cd /tmp/hook-test
git init
mkdir .claude
cat > .claude/settings.json << 'EOF'
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "date >> /tmp/sessionstart_log.txt && echo 'SessionStart fired'"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "date >> /tmp/posttooluse_log.txt"
          }
        ]
      }
    ]
  }
}
EOF
git add -A && git commit -m "init"

2. Confirm the hook fires on startup:

rm -f /tmp/sessionstart_log.txt
cd /tmp/hook-test && claude --init-only
cat /tmp/sessionstart_log.txt
# Expected: timestamp printed (hook fired)
# Actual:   timestamp printed (PASS)

3. Test /clear in an interactive session:

rm -f /tmp/sessionstart_log.txt /tmp/posttooluse_log.txt
cd /tmp/hook-test && claude

Inside the session:

  • Ask Claude to run any bash command (e.g. echo hello)
  • Run /clear
  • Exit and check the log files:
cat /tmp/posttooluse_log.txt   # Expected: exists (PostToolUse fired)
wc -l /tmp/sessionstart_log.txt # Expected: 2 lines (startup + clear)
                                 # Actual:   1 line  (startup only - clear trigger never fired)

Expected behaviour

Per the hooks documentation, SessionStart fires on four triggers:

| Source | Trigger | Status |
|--------|---------|--------|
| startup | New session / --init-only | Works |
| resume | --resume / --continue | Not tested |
| clear | /clear | Broken |
| compact | Auto/manual compaction | Not tested |

Actual behaviour

  • startup trigger: hook fires (verified via --init-only and side-effect file creation)
  • clear trigger: hook does not fire (side-effect file contains only the startup entry - no second entry from /clear)
  • PostToolUse hooks in the same settings file work correctly, confirming settings are loaded

Relationship to #10373

Issue #10373 reports a different failure mode for SessionStart hooks: hooks execute on startup but their output is not injected into conversation context. In this issue, the hook command itself never executes when triggered by clear (the side-effect file contains only the startup entry, with no second entry from /clear). Comments on #10373 suggest the behaviour may be intermittent and version-dependent.

Source code analysis

The /clear command handler does call executeSessionStartHooks("clear"):

// Simplified from minified cli.js
async function clearConversation({setMessages, ...}) {
  await executeSessionEndHooks("clear", ...);
  setMessages(() => []);           // clear messages
  // ... reset state, new session ID, new session file ...
  let msgs = await executeSessionStartHooks("clear");
  if (msgs.length > 0) setMessages(() => msgs);
}

The hook runner (kb()) has several early-return guards that could silently prevent execution:

async function* kb({hookInput, ...}) {
  if (disableAllHooks()) return;                        // policy check
  if (process.env.CLAUDE_CODE_SIMPLE) return;           // env var check
  let matched = getMatchingHooks(appState, agentId, eventName, hookInput);
  if (matched.length === 0) return;                     // no hooks found
  if (signal?.aborted) return;                          // abort check
  if (!workspaceTrustAccepted()) return;                // trust check
  // ... execute hooks ...
}

Notably, getMatchingHooks() wraps its entire body in try { ... } catch { return []; }, which would silently swallow any error during hook resolution and make it appear as if no hooks matched.

The exact failure point is unclear from the minified source, but the hook command itself never executes (confirmed by the absence of the side-effect file).

Workaround

Add instructions to CLAUDE.md to manually read the target file at session start, since CLAUDE.md is reliably loaded on both startup and /clear.

View original on GitHub ↗

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