[BUG] Double SessionStart:compact + "Prompt is too long" on session initialization race

Resolved 💬 3 comments Opened Feb 19, 2026 by ghost Closed Mar 25, 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?

On every new Claude Code session start, the "Prompt is too long" error fires twice
and the SessionStart:compact hook executes twice in the same initialization sequence.
This happens consistently and reproducibly when 40+ plugins are enabled.

The symptom is visible in the conversation immediately: before the user has typed
anything, two identical "Prompt is too long" banners appear back-to-back, and any
configured SessionStart hooks execute twice. Hook side-effects (file writes, API
calls, state mutations) therefore happen twice every single session.

The root cause appears to be a race condition between two separate overflow-detection
code paths:

  1. The session-start initialization path, which loads plugin CLAUDE.md files,

MCP server tool registrations, and skill definitions into the system prompt,
and checks whether the resulting prompt exceeds the compaction threshold.

  1. The incoming-message handler, which also checks for context overflow when

the first user message (or a message sent during startup) arrives.

When both paths execute close together in time — which happens when a message
arrives while initialization is still in progress — both independently detect that
the context is over threshold and both independently trigger compaction. Neither
path checks whether the other has already started or completed compaction.

The result is that SessionStart:compact fires twice. If a user has a SessionStart
hook configured (e.g., to inject context, clear files, call an API), that hook runs
twice on every session, which is incorrect behavior.

This is distinct from simply having a large system prompt: the double-fire only
occurs when a message arrives during the initialization window. In a slow startup
with no early message, only one compaction fires.

What Should Happen?

Exactly one compaction event should fire per session start, regardless of when
the first user message arrives relative to initialization completion.

The two overflow-detection paths (session-start and incoming-message) should be
mutually exclusive or guarded by a flag: once one has triggered compaction, the
other must detect that compaction is already in progress or already completed and
skip its own trigger.

The "Prompt is too long" banner should appear at most once. Any configured
SessionStart hook should execute exactly once.

Error Messages/Logs

The following appears verbatim at the top of every new session (before any user
  input), with no delay between the two occurrences:

    SessionStart:compact hook success: Success
    Prompt is too long
    SessionStart:compact hook success: Success
    Prompt is too long

  Both lines are identical. The hook reports "success" twice, meaning it fully
  executed twice — it did not error or short-circuit on the second run.

  Note: "SessionStart:compact" is the name of a custom Stop hook registered in
  ~/.claude/settings.json. The hook name is user-configured and appears in the
  banner as "SessionStart:<hook-name> hook success: <result>". The double-fire
  is the issue, not the hook name itself.

Steps to Reproduce

  1. Enable 40 or more plugins in ~/.claude/settings.json under "enabledPlugins".

Each plugin contributes one or more CLAUDE.md files and skill definitions to
the session's system prompt. 40+ plugins reliably pushes the prompt over the
compaction threshold at startup.

  1. (Optional but makes the double-fire more reliable) Set the following in the

"env" section of ~/.claude/settings.json to lower the compaction threshold:
"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "70"
This causes compaction to trigger at 70% of context limit instead of the
default ~95%, making overflow happen earlier and more predictably.

  1. Start a new Claude Code session (claude in a terminal, or open a new

conversation in the Claude Code IDE extension).

  1. Send any message immediately — before waiting for the initialization

"thinking" indicator to disappear. Typing quickly after the prompt appears
is sufficient; you do not need precise timing. With 50 plugins loaded, the
initialization window is long enough that a normal typist reliably hits it.

  1. Observe the output above the assistant's first response:
  • "Prompt is too long" appears twice
  • If a SessionStart hook is configured, it reports success twice
  • The assistant's actual response follows normally

Alternatively, without sending an early message, simply starting a session with
50+ plugins may be sufficient to reproduce the double-fire on its own, as the
initialization sequence itself may trigger both paths without any user message.
The early-message path makes the race more reliable.

Claude Model

Not sure / Multiple models

Is this a regression?

I don't know

Last Working Version

_No response_

Claude Code Version

2.1.47 (Claude Code)

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Terminal.app (macOS)

Additional Information

TECHNICAL CONTEXT — HOW THE SYSTEM PROMPT GROWS

Each enabled plugin contributes the following to the system prompt at session start:

  • Its CLAUDE.md file (instructions, behavioral rules, project context)
  • Its skill definitions (each skill becomes an entry in the available-skills list

shown in the system-reminder block)

  • Its MCP server tool registrations (each MCP tool gets a tool-description block)

With 50 plugins enabled, the cumulative system prompt from plugin content alone
is large enough to exceed the autocompact threshold before the first user message
is processed. This is the underlying condition that makes the race reproducible.

RELEVANT CONFIGURATION (~/.claude/settings.json)

"env": {
"CLAUDE_AUTOCOMPACT_PCT_OVERRIDE": "70"
}

This overrides the default compaction threshold from ~95% to 70% of context limit.
With this setting, the system prompt from 50 plugins crosses the threshold during
initialization itself, not just when conversation history accumulates.

SEQUENCE OF EVENTS (hypothesized)

t=0 Session starts. Claude Code begins loading plugin CLAUDE.md files,
MCP server registrations, skill definitions into the system prompt.

t=1 Initialization path assembles the full system prompt. It detects that
the prompt exceeds the threshold (70% of context limit). It queues or
begins a compaction event. SessionStart:compact hook fires (first time).
"Prompt is too long" banner appears (first time).

t=2 User types and sends a message. The message arrives while the
initialization path's compaction event has not yet fully committed
its state (or the incoming-message handler runs before it checks a
"compaction already in progress" flag).

t=3 The incoming-message handler checks context size. It independently
sees that the context is over threshold (the first compaction may not
have completed yet, or the state was not yet updated). It triggers
its own compaction event. SessionStart:compact hook fires (second time).
"Prompt is too long" banner appears (second time).

t=4 Normal session proceeds.

IMPACT OF THE DOUBLE-FIRE

For users with idempotent SessionStart hooks (e.g., display-only or read-only),
the double-fire is cosmetically annoying but functionally harmless.

For users with non-idempotent hooks — hooks that write files, post to APIs,
increment counters, or mutate state — the double-fire causes real bugs: the
side effect executes twice every session.

WORKAROUND

Reduce the number of enabled plugins below the threshold where the system prompt
overflows at startup (approximately 30-35 plugins with default settings, or fewer
with a lowered CLAUDE_AUTOCOMPACT_PCT_OVERRIDE). This does not fix the race
condition but prevents it from triggering.

ENVIRONMENT

  • macOS Darwin 25.2.0
  • ~50 plugins enabled
  • CLAUDE_AUTOCOMPACT_PCT_OVERRIDE: 70
  • Shell: zsh

View original on GitHub ↗

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