Bug: `beforeSubmitPrompt` hook `updated_input` is silently stripped — modified prompt never reaches the model

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Cursor version: 3.2.7
OS: macOS
Composer mode: Agent


Summary

Bug: beforeSubmitPrompt hook updated_input is silently stripped — modified prompt never reaches the model

The beforeSubmitPrompt hook fires correctly and returns a valid updated_input.prompt. The hook log confirms the response is well-formed. However, the agent transcript shows only the original prompt wrapped in <user_query> — the injected content is completely stripped before reaching the model.

Steps to Reproduce

1. Create .cursor/hooks.json:

{
    "version": 1,
    "hooks": {
        "beforeSubmitPrompt": [
            {
                "command": "/usr/local/opt/[email protected]/bin/php -d xdebug.mode=off .cursor/hooks/trait-harness.php"
            }
        ]
    }
}

2. Create .cursor/hooks/trait-harness.php:

#!/usr/bin/env php
<?php
declare(strict_types=1);
if (function_exists('xdebug_disable')) { xdebug_disable(); }
ini_set('xdebug.mode', 'off');

$input  = json_decode(file_get_contents('php://stdin'), true);
$prompt = $input['prompt'] ?? '';

echo json_encode([
    'action'        => 'allow',
    'updated_input' => [
        'prompt' => '[INJECTED CONTEXT] ' . $prompt,
    ],
]);

(Simplified to minimal reproducer — original script does keyword detection and loads skill node files, but the stripping happens regardless of prompt content.)

3. Verify hook works from terminal:

printf '%s' '{"prompt":"test prompt","hook_event_name":"beforeSubmitPrompt"}' \
  | /usr/local/opt/[email protected]/bin/php -d xdebug.mode=off .cursor/hooks/trait-harness.php

Output (hook works correctly):

{"action":"allow","updated_input":{"prompt":"[INJECTED CONTEXT] test prompt"}}

4. Submit any prompt in Cursor Agent mode.

5. Check hook log — Cursor fires the hook and receives correct updated_input:

{
  "hook_event_name": "beforeSubmitPrompt",
  "cursor_version": "3.2.7",
  "prompt": "System szpitalny. Klasy MedicalRecord potrzebują soft delete."
}

Hook response logged by Cursor:

{
  "action": "allow",
  "updated_input": {
    "prompt": "[INJECTED CONTEXT] System szpitalny. Klasy MedicalRecord potrzebują soft delete."
  }
}

6. Check agent transcript at:
~/.cursor/projects/<workspace>/agent-transcripts/<session-id>/<session-id>.jsonl

{"role":"user","message":{"content":[{"type":"text","text":"<user_query>\nSystem szpitalny. Klasy MedicalRecord potrzebują soft delete.\n</user_query>"}]}}

The injected [INJECTED CONTEXT] prefix is gone. The model receives only the original prompt.

Expected Behavior

updated_input.prompt returned by the hook replaces the prompt sent to the model. The agent transcript should contain the modified prompt inside <user_query>.

Operating System

MacOS

Version Information

Version: 3.2.7 (Universal)
VSCode Version: 1.105.1
Commit: 251fe2954fc02bde617557cee00fcc1d27a6e590
Date: 2026-04-22T23:35:05.835Z
Layout: glass
Build Type: Stable
Release Track: Nightly
Electron: 39.8.1
Chromium: 142.0.7444.265
Node.js: 22.22.1
V8: 14.2.231.22-electron.0
OS: Darwin x64 25.4.0

For AI issues: which model did you use?

Auto

Additional Information

Actual behavior

Cursor fires the hook, logs the updated_input response, but strips the modification before sending to the model. The transcript contains only the original unmodified prompt.


Additional details

  • The hook does fire — confirmed by hook_event_name: beforeSubmitPrompt in logs
  • The hook does return valid JSON with updated_input — confirmed by terminal test
  • The stripping happens between hook output and model input
  • Reproducible across multiple sessions (session IDs: e0d2cf8a, 3e1246c6)
  • deny action works correctly (blocks the prompt) — only updated_input modification is broken
  • Workaround: alwaysApply: true rule with inline content reaches the model, but lacks per-prompt selectivity

Use case context

This feature is central to building context-injection harnesses — loading only the relevant portion of a decision graph based on prompt content, rather than always injecting the full context. Without working updated_input, the only option is alwaysApply rules which load regardless of relevance, wasting context window on every request.

Happy to provide full transcript files or additional debug output if useful.

Does this stop you from using Cursor

No - Cursor works, but with this issue

Thanks for the detailed report and the minimal reproducer.

This is actually expected behavior rather than a bug. The beforeSubmitPrompt hook currently only supports two output fields:

  • continue (boolean) — block or allow the submission

  • user_message (string) — message shown to the user when blocked

It does not support updated_input or any field that modifies the prompt text. The updated_input field is supported on preToolUse (for modifying tool parameters), which may be where the expectation came from. Because the hook validator doesn’t reject unknown fields, the JSON passes without error but the extra fields are ignored.

For your use case of injecting context based on prompt content, a couple of alternatives:

  1. .cursor/rules with alwaysApply: true — this is the most reliable way to inject context into every prompt today. It lacks per-prompt selectivity, but the context does reach the model end-to-end.

  2. sessionStart hook with additional_context — can inject context at the start of a conversation. Better suited for session-level context than per-prompt, and there are currently some known gaps in this path as well.

There’s an existing feature request thread for exactly what you’re describing — adding context-injection support to beforeSubmitPrompt:

Add additional_context to beforeSubmitPrompt hook output

I’d recommend adding your use case there. Community engagement on that thread helps our product team gauge demand and prioritize accordingly.

For the full reference on what each hook supports, see the Hooks documentation.

Fantastic write-up and minimal reproducer!

I want to flag for the Cursor team that this is clearly part of a much broader, systemic issue with the hook architecture’s context-injection pipeline.

We are seeing the exact same “silently stripped / discarded output” behavior across other hooks, most notably postToolUse and sessionStart. Just like your case, the hooks fire, valid JSON is returned and logged by Cursor, but the injected context is completely dropped before it ever reaches the Agent’s context window.

It seems the plumbing between the hook manager and the model prompt builder is broken across the board, regardless of the hook type.

Here is the related thread detailing the exact same failure pattern for postToolUse:

Hopefully, highlighting that this affects beforeSubmitPrompt, postToolUse, and sessionStart will help the engineering team track down the shared root cause in the prompt pipeline.