Project sessionStart hook never runs for the chat a window creates while it opens (chat created before project hooks load, step never replayed)

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

A project sessionStart hook in .cursor/hooks.json never runs for the chat that a window creates while it is opening. Cursor creates that chat before the window’s hooks service has loaded the project hooks, so the hasHookForStep(sessionStart) check at chat creation returns false, and nothing runs the step for that chat afterward. Messages in that chat reach the model without the hook’s additional_context, and the hook’s env is never set. A chat created later in the same window runs the hook normally.
This differs from the confirmed additional_context race in 158452 (also topics 157141 and 167274): there the hook runs and its output is dropped; here the hook never runs. It matches the pattern of 172939, where workspaceOpen is dispatched before project hooks are registered and never replayed.
Timeline from the Hooks output channel and each chat’s stored createdAt (UTC, single window):

  • 21:50:12.352: hooks service starts without a workspace folder (“No workspace folder found, project hooks will not be available”)
  • 21:50:14.387: chat A created (createdAt)
  • 21:50:14.510: hooks service for the workspace starts
  • 21:50:14.801: “Loaded 13 project hook(s) for steps: sessionStart, preToolUse, beforeSubmitPrompt, stop, sessionEnd”
  • 21:50:20.603: first message in chat A; the first log entry for its conversation_id is beforeSubmitPrompt, and the log has no “Hook step requested: sessionStart” for chat A
  • 21:51:09.488: chat B created in the same window; “Hook step requested: sessionStart” logged at 21:51:09.488 as well; all 5 hooks run
  • 21:51:11.308: “Merged 5 valid response(s) for step sessionStart” (chat B)
    Our sessionStart hook also writes a marker file per session_id: chat B has one, chat A has none.
    In workbench.desktop.main.js and workbench.glass.main.js, the chat-creation path is the only place that runs the step; each bundle has one executeHookForStep(…sessionStart call and one hasHookForStep(…sessionStart) check:
this.cursorHooksService.initializeSessionTracking(g,x),this.cursorHooksService.hasHookForStep(Du.sessionStart)&&(async()=>{ … executeHookForStep … })();let A;try{A=await this.composerDataService.appendComposer_DO_NOT_CALL_UNLESS_YOU_KNOW_WHAT_YOURE_DOING(E)}

Steps to Reproduce

From the occurrence above; not yet repeated in a clean project.

  1. Add a project .cursor/hooks.json with a sessionStart hook that appends session_id to a log file and prints {"additional_context": "Session marker: TEST-123"}.
  2. Open the project in a new window. In our log, the window’s hooks service started without a folder and loaded the project hooks about 2.4 s later.
  3. Without creating a new chat, send a message in the chat the window shows after opening.
  4. The Hooks output channel shows no “Hook step requested: sessionStart” for that conversation, and the log file has no line for its session_id.
  5. Create a new chat in the same window and send a message: the hook runs.

Expected Behavior

sessionStart runs once for every chat, including a chat created before the hooks configuration finishes loading. For example, wait for the hooks service to load before the hasHookForStep check, or run a skipped sessionStart after project hooks are registered and before the chat’s first request.

Operating System

MacOS

Version Information

Cursor 3.27.12 (commit df132ffba90dfc15a12e6b8f020ba4e5d026c4b0), VS Code 1.128.0, macOS 27.0 (26A428), arm64. Structured logs report layout: unifiedAgent.

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hey @universe,

Your reading matches what we see. Right now the sessionStart hook is only started at the moment a chat is created, and it is skipped if the project hooks haven’t finished loading yet. Nothing runs it again later, so the chat a window creates while it opens never gets the hook’s additional_context or env. Chats created after the hooks load work normally, as you saw with chat B. I’ve flagged this to the team.

In the meantime, two ways around it:

  1. After the window opens, wait until Output > Hooks shows the “Loaded N project hook(s)” line, then start a new chat (Cmd+N or the + button) instead of using the one that was already open.
  2. If you only need the context (not env), you can also return it from a beforeSubmitPrompt hook, for example {"continue": true, "additional_context": "Session marker: TEST-123"}. Sending waits for hooks to finish loading before that hook runs, so it isn’t affected by this timing. It runs on every message, so keep it small or only emit it the first time your script sees a given conversation_id.