Native postToolUse hooks accept and log additional_context successfully, but the injected context is not surfaced to the model

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

A native postToolUse hook configured in .cursor/hooks.json runs successfully, returns valid JSON with an additional_context field, and Cursor’s Hooks log shows that output correctly. However, the model does not behave as if it received the injected context.

I verified this with a minimal smoke-test hook that removes normal hook logic as a variable. The hook only fires for one dedicated test file path and returns a single unmistakable additional_context string telling the model to say exactly POST_TOOL_SMOKE_TEST_RECEIVED if the context is visible. Cursor logs the hook as successful and shows the exact payload, but the model never acknowledges or reacts to it.

This suggests the issue is not invalid JSON, matcher configuration, or hook script logic. The problem appears to be that postToolUse additional_context is accepted by the hook runner but is not actually surfaced to the model.

Steps to Reproduce

  1. Create a native Cursor hook in .cursor/hooks.json:

{
“version”: 1,
“hooks”: {
“postToolUse”: [
{
“matcher”: “Write”,
“command”: “bash .cursor/hooks/posttooluse-smoke-test.sh”
}
]
}
}

  1. Create the hook script at .cursor/hooks/posttooluse-smoke-test.sh:

#!/bin/bash
set -euo pipefail

INPUT_JSON=$(cat)
FILE_PATH=$(printf ‘%s’ “$INPUT_JSON” | python3 -c ’
import json, sys
try:
payload = json.load(sys.stdin)
tool_input = payload.get(“tool_input”, {})
print(tool_input.get(“file_path”, “”) or tool_input.get(“path”, “”))
except Exception:
print(“”)
')

case “$FILE_PATH” in
*/.cursor-posttooluse-smoke-test.txt)
python3 - <<‘PY’
import json

print(json.dumps({
“additional_context”: (
"CURSOR_POSTTOOLUSE_SMOKE_TEST_ACTIVE. "
"If you can read this injected context, explicitly say exactly: "
“‘POST_TOOL_SMOKE_TEST_RECEIVED’.”
)
}))
PY
;;
*)
echo ‘{}’
;;
esac

  1. Make the script executable.
  2. In Cursor Agent, create a file named .cursor-posttooluse-smoke-test.txt.
  3. Open the Hooks log and confirm the postToolUse hook ran.
  4. Check whether the model responds with POST_TOOL_SMOKE_TEST_RECEIVED.

Expected Behavior

The model should receive the additional_context string injected by the native postToolUse hook and react to it on the next continuation. In this repro, it should explicitly say POST_TOOL_SMOKE_TEST_RECEIVED.

Operating System

MacOS

Version Information

Version: 2.6.20
VSCode Version: 1.105.1
Commit: b29eb4ee5f9f6d1cb2afbc09070198d3ea6ad760
Date: 2026-03-17T01:50:02.404Z
Build Type: Stable
Release Track: Default
Electron: 39.8.1
Chromium: 142.0.7444.265
Node.js: 22.22.1
V8: 14.2.231.22-electron.0
OS: Darwin arm64 24.6.0

For AI issues: which model did you use?

Model name: gpt-5.4-high

Additional Information

Relevant observations:

  • This is a native Cursor hook in .cursor/hooks.json, not a third-party Claude compatibility hook.
  • The hook runner appears to work correctly.
  • The additional_context payload is logged exactly as expected.
  • preToolUse deny behavior works correctly in the same environment.
    stop hooks with followup_message also work correctly in the same environment.
  • The issue appears specific to postToolUse additional_context.

Example of the logged hook output:

{
“additional_context”: “CURSOR_POSTTOOLUSE_SMOKE_TEST_ACTIVE. If you can read this injected context, explicitly say exactly: ‘POST_TOOL_SMOKE_TEST_RECEIVED’.”
}

But the model never outputs POST_TOOL_SMOKE_TEST_RECEIVED, which strongly suggests the injected context is not actually reaching the model.

Does this stop you from using Cursor

Sometimes - I can sometimes use Cursor

Hi @Jim_Beno

Thank you for this incredibly well-documented report – the methodical testing with your hooks output really helps.

I can confirm this is a bug. The additional_context field in postToolUse responses is accepted and validated by the hook runner (which is why your logs show it working), but the value is never actually injected into the model’s conversation context. The plumbing on the consumer side was started but not completed.

Specifically:

  • For non-MCP tools, the postToolUse hook runs as fire-and-forget – the response (including additional_context) is discarded

  • For MCP tools, the response is awaited but only updated_mcp_tool_output is consumed; additional_context is still ignored

  • The only hook where additional_context currently works end-to-end is sessionStart

I’ve flagged this with our engineering team for tracking. Your observation that sessionStart works correctly while postToolUse does not is consistent with the code – they have different execution paths and only sessionStart has the full plumbing connected.

In the meantime, a potential workaround depending on your use case: if the context you need to inject is relatively static or can be determined at session start, you could use sessionStart with additional_context to pre-load it. I understand this won’t cover the dynamic per-tool-use case you’re building for, but it may help for some scenarios.

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

Cursor agent is unable to access additional_context when in the output of postToolUse

In this conversation you can even see the confirmation from the leaked thinking from composer 2

Steps to Reproduce

Create a postToolUse hook that returns any additional_context > the agent is unable to access that data

here’s an example

{
  "additional_context": "<codex_thread_context>\nthreadId: 019d37c4-df88-7111-9e7e-c0aab0526249\n</codex_thread_context>",
  "updated_mcp_tool_output": {
    "content": [
      {
        "type": "text",
        "text": "codex-thread-context-tag-test"
      }
    ],
    "isError": false,
    "structuredContent": {
      "threadId": "019d37c4-df88-7111-9e7e-c0aab0526249",
      "thread_id": "019d37c4-df88-7111-9e7e-c0aab0526249"
    }
  }
}

Operating System

Windows 10/11

Version Information

Version: 2.6.22 (system setup)
VSCode Version: 1.105.1
Commit: c6285feaba0ad62603f7c22e72f0a170dc8415a0
Date: 2026-03-27T15:59:31.561Z
Build Type: Stable
Release Track: Early Access
Electron: 39.8.1
Chromium: 142.0.7444.265
Node.js: 22.22.1
V8: 14.2.231.22-electron.0
OS: Windows_NT x64 10.0.26200

For AI issues: add Request ID with privacy disabled

7c09b0d1-174f-4b25-bbf9-c540cb37b7b6

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hay @mohitjain !

Do you have some updates?

For now, the most dependable way to inject static context is through a .cursor/rules file. Not a replacement for dynamic per-tool-use context, but it works end-to-end for anything that doesn’t need to change between tool calls.

I’ll post here when there’s progress on the fix.

Adding data points on Cursor 3.1.15 (macOS, 2026-04-21). The postToolUse input side also looks unpopulated, on the same path where the response is discarded — so even fixing the output plumbing wouldn’t be sufficient on its own.

Hook script logs process.argv, CURSOR_* env vars, and stdin length on every invocation. Representative entries:

[2026-04-21T21:53:59.071Z] spawn pid=25047 argv=[] env=

[2026-04-21T21:53:59.077Z] stdin-end bytes=0

[2026-04-21T21:55:37.036Z] spawn pid=26394 argv=[] env=

[2026-04-21T21:55:37.044Z] stdin-end bytes=0

Every invocation: argv empty, no CURSOR_* env vars present, stdin closes with 0 bytes before the hook can read anything. So the hook has no way to know which tool fired, what the input was, or what the response was — it can emit a fixed-shape additional_context but nothing conditional on the event.

The same script installed as a beforeShellExecution hook on the same Cursor version receives full payloads on stdin (~600 bytes with command, session_id, cwd, hook_event_name, etc.), so the stdin plumbing works in other hook types — postToolUse specifically isn’t forwarding anything.

Separately, firing is sporadic. During a ~15-minute session with many Write/Edit tool calls, the hook fires only ~10 times total — often as bursts of 2–3 simultaneous invocations at the same millisecond, then quiet for minutes. Matches the pattern reported on afterFileEdit hook not firing reliably, missing events on batch edits and mid-session for afterFileEdit, possibly a shared root cause.

So three independent postToolUse gaps on 3.1.15:

  1. Intermittent firing — events dropped during bursts and mid-session
  2. Empty input payload — no data via argv, env, or stdin
  3. Output discarded — the fire-and-forget response-handling path this thread already covers

Flagging so all three can be scoped together — a fix that only lands (3) would leave the hook effectively unusable because of (2).

Thanks for the detailed breakdown, Andy. I tested these and could only reproduce the output side — additional_context is still accepted and merged, but never surfaced to the model. That bug persists.

The empty stdin and intermittent firing issues I wasn’t able to reproduce. Can you update and see if those two still occur? You can update to the latest stable version if you haven’t already, or if you’d like to test the very latest fixes, search for ‘Nightly’ in your settings — that gives you early access to our latest builds.

+1 to everything @Andy_Lockey described. I am experiencing the exact same behavior on version 3.2.9 on macOS.

The detailed breakdown in comment #11 is spot on. Because the input payload (argv, env, stdin) is completely empty AND the output is discarded, the postToolUse hook is practically unusable for any real-world workflow right now. You simply cannot build conditional logic if the hook has zero awareness of what tool just fired.

This is a major blocker for those of us trying to build custom integrations and extend the agent’s context. I really hope the team can address all three aspects of this issue (input payload, output routing, and intermittent firing) in an upcoming patch, rather than just fixing the output discarding.

Update: Support created ticket T-C20310 for this issue and forwarded it to the engineering team. Hoping for a fix soon!

In my test, a native `postToolUse` hook for `Read` returns valid JSON with `additional_context`, but the model never receives it. I also added a probe hook that intentionally sleeps for 10 seconds and logs `START` and `END`.

Observed behavior:

1. The agent calls `Read`.

2. The `Read` result is returned to the agent immediately.

3. The hook log shows `START`.

4. The agent can continue reasoning / respond before the hook finishes.

5. About 10 seconds later, the hook log shows `END`.

6. The hook returns valid JSON with `additional_context`.

7. The model never sees or reacts to that injected context.

So this appears to be two related symptoms:

- `postToolUse` appears to run asynchronously / fire-and-forget for this tool call.

- The returned `additional_context` is accepted/logged by the hook runner but is not surfaced to the model.

This matches the original report: the hook runner works, the payload is valid, but the injected context does not reach the model.

Expected behavior, based on the docs, would be:

`tool result → postToolUse hook finishes → additional_context is injected → model continues with that context`

Actual behavior appears to be:

`tool result → model continues → postToolUse hook finishes later → additional_context is not visible to the model`

This makes `postToolUse.additional_context` unusable for workflows where the agent must receive analysis or metadata after a tool call before continuing.

Hey @Bogdan_Parhomenko , thanks for the probe test — the 10-second sleep confirming the model continues before the hook finishes matches what we see in the code.

The empty stdin issue you and Andy are both hitting is also noted. We couldn’t reproduce it previously, but with two independent confirmations across different versions (3.1.15, 3.2.9), I’ve updated the tracking ticket to reflect all three issues need to be addressed together.

No fix has shipped yet, and I don’t have a timeline to share. I’ll post here when there’s progress.

Just an update for anyone tracking these hook issues: the payload problems are not limited to `postToolUse`.

On version 3.2.18, I just discovered that the `preToolUse` hook is also dropping critical tool call arguments. Specifically, when the agent uses `ReadFile`, the hook payload strips all line range parameters (`offset`, `limit`) and converts them to `null`.

I’ve created a separate bug report for it here, but linking it because it strongly suggests the entire serialization pipeline between the Agent and the Hook runner is currently broken: preToolUse hook drops line range parameters (offset/limit) on ReadFile tool calls (v3.2.18)

Hey @Bogdan_Parhomenko - thanks for flagging the preToolUse issue here. I saw your separate thread and another member of our team already confirmed the bug and filed it internally. The ReadFile hook payload only forwards file_path today and drops offset/limit, which is a straightforward gap in that tool’s hook adapter.

You’re right that this and the postToolUse issues point to the same class of problem in the hooks pipeline. Both are being tracked separately so they can be fixed independently, but the team is aware of the shared pattern. No updates on the original three issues from this thread yet. I’ll continue posting here when anything ships.

Thanks for the update and for connecting the dots, @mohitjain! I appreciate the team looking at the broader pattern across the pipeline while tracking the fixes independently. I’ll hold tight for updates on the postToolUse side of things whenever they are ready.