[BUG] SessionStart hooks don't execute on first run with GitHub marketplace plugins

Resolved 💬 5 comments Opened Nov 4, 2025 by wehriam Closed Jan 9, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] This is a single bug report (please file separate reports for different bugs)
  • [x] I am using the latest version of Claude Code

What's Wrong?

SessionStart hooks do not execute on the first Claude Code run when plugins are loaded from GitHub marketplaces via extraKnownMarketplaces in .claude/settings.json. The hooks only execute on subsequent runs after the marketplace has been cached locally.

The test plugin's SessionStart hook should create a file named SESSION_START_HOOK_COMPLETE in the working directory when Claude Code starts. On the first run, this file is not created. On subsequent runs, the file is created successfully.

Note: This issue only occurs in interactive mode where process.stdout.isTTY is true. Non-interactive mode (using -p flag) does not load marketplaces at all, which is separate behavior.

What Should Happen?

SessionStart hooks should execute every time Claude Code starts a session, including the first run when a marketplace is newly configured.

Error Messages/Logs

No error messages are displayed. The hook silently fails to execute on first run.

Steps to Reproduce

Quick Reproduction (Automated Test Script)

  1. Clone the demonstration repository:

``bash
git clone https://github.com/goodfoot-io/plugin-load-timing-issue-reproduction.git
cd plugin-load-timing-issue-reproduction
``

  1. Run the automated test script:

``bash
chmod +x ./test-sessionstart-hook.sh
./test-sessionstart-hook.sh
``

Expected output:

==========================================
SessionStart Hook Test Script
==========================================

[Step 1] Running first Claude command in interactive mode...
   Started Claude with pseudo-TTY (PID: 90703), waiting 10 seconds...
   Killed Claude process

[Step 2] Checking for SESSION_START_HOOK_COMPLETE file after first run...
❌ SESSION_START_HOOK_COMPLETE does NOT exist after first run

[Step 3] Running second Claude command...
   Started Claude with pseudo-TTY (PID: 90915), waiting 10 seconds...
   Killed Claude process

[Step 4] Checking for SESSION_START_HOOK_COMPLETE file after second run...
✅ SESSION_START_HOOK_COMPLETE exists after second run
   Content:
   SessionStart hook executed at Tue Nov  4 01:57:33 PM EST 2025

[Step 5] Removing plugin...
✔ Successfully uninstalled plugin: sessionstart-hook-demonstration

[Step 6] Removing marketplace...
✔ Successfully removed marketplace: plugin-load-timing

[Step 7] Cleaning up SESSION_START_HOOK_COMPLETE file...
✅ Removed SESSION_START_HOOK_COMPLETE

==========================================
Test script complete!
==========================================

Manual Reproduction

  1. Configure .claude/settings.json with the GitHub marketplace and enabled plugin:

``json
{
"extraKnownMarketplaces": {
"plugin-load-timing": {
"source": {
"source": "github",
"repo": "goodfoot-io/plugin-load-timing-issue-reproduction-marketplace"
}
}
},
"enabledPlugins": {
"sessionstart-hook-demonstration@plugin-load-timing": true
}
}
``

  1. Ensure clean state (no cached marketplace):

``bash
claude plugin marketplace remove "plugin-load-timing" 2>/dev/null || true
claude plugin remove "sessionstart-hook-demonstration@plugin-load-timing" 2>/dev/null || true
rm -f SESSION_START_HOOK_COMPLETE
``

  1. Run Claude Code in interactive mode:

``bash
claude
``

Wait for Claude to start, then exit (press Escape twice or Ctrl+C).

  1. Verify SessionStart hook did not execute:

``bash
ls SESSION_START_HOOK_COMPLETE # File should NOT exist
``

  1. Run Claude Code again:

``bash
claude
``

Wait for Claude to start, then exit (press Escape twice or Ctrl+C).

  1. Verify SessionStart hook now executes:

``bash
cat SESSION_START_HOOK_COMPLETE # File should exist with timestamp
``

Claude Model

Sonnet (default)

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.0.32

Platform

Anthropic API

Operating System

Ubuntu/Debian Linux

Terminal/Shell

VS Code integrated terminal

Additional Information

About the Test Plugin

The plugin contains a SessionStart hook that creates a file named SESSION_START_HOOK_COMPLETE in the current working directory with a timestamp. This provides clear evidence of whether the hook executed.

Repositories

Root Cause

Claude Code loads marketplaces asynchronously. The SessionStart hook fires before the asynchronous marketplace fetch completes on first run. On subsequent runs, the cached marketplace loads synchronously and plugins register before SessionStart fires.

Timeline (First Run):

  1. Settings loaded
  2. Marketplace fetch begins (async, GitHub download)
  3. SessionStart hooks fire (plugins not yet registered)
  4. Marketplace fetch completes (too late)

Timeline (Subsequent Runs):

  1. Settings loaded
  2. Cached marketplace loads (instant)
  3. Plugins registered
  4. SessionStart hooks fire (plugins available)

Impact

  • Initialization tasks in SessionStart hooks fail on first run
  • Inconsistent behavior between first and subsequent runs
  • No error indication when hooks do not execute

View original on GitHub ↗

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