[BUG] "Notification" hook doesn't work in VS Code extension "native UI" mode

Status Fixed / completed
Maintainer reply None cached
Activity 14 comments · opened Oct 5, 2025 · closed Aug 19, 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?

Claude Code version 2.0.8, in VS Code extension's "Native UI" mode.

The "Notification" hook doesn't work any more: when Claude Code pauses to ask for my intervention, the hook doesn't work, because I set up a hook to play a sound at that moment. I can't hear that sound any more.

But in the CLI mode (version 2.0.8), I could hear that sound.

What Should Happen?

"Notification" hook should be engaged.

Error Messages/Logs

Steps to Reproduce

Put the following into ~/.claude/settings.json:

{
  "hooks": {
    "Notification": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "afplay -v 3 /System/Library/Sounds/Blow.aiff"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "afplay -v 3 /System/Library/Sounds/Bottle.aiff"
          }
        ]
      }
    ]
  }
}

And do something that would make Claude Code ask for human intervention.

Claude Model

None

Is this a regression?

Yes, this worked in a previous version

Last Working Version

_No response_

Claude Code Version

2.0.8

Platform

Anthropic API

Operating System

macOS

Terminal/Shell

Other

Additional Information

It happened in the new "native UI" of the VS Code extension.

View original on GitHub ↗

13 Comments

redbeard · 10 months ago

Second this -- works in terminal, fails in vscode ui mode.

bsdev90 · 10 months ago

Same here (Cursor, Windows 11). Stop hook works though

joemillerpe-ux · 9 months ago

Confirmed: Notification Hooks Not Firing for Permission Prompts

I can verify this issue affects permission_prompt notifications specifically.

Configuration

  • Empty matcher "matcher": "" to catch all notifications
  • Windows 11, VS Code extension
  • Enhanced script that logs notification type and message

Testing Results

Manual script test: Works perfectly when invoked directly
PreToolUse/PostToolUse hooks: Fire correctly for Read tool
Notification hooks: Never fire despite multiple permission prompts

Evidence

  • Cleared .claude/settings.local.json allow list to force prompts
  • Confirmed seeing 3+ permission prompts in UI
  • Zero notification hook invocations logged
  • Tested after full restart - same result
  • Hook script verified working via manual JSON piping

The notification hook system is completely non-functional for permission_prompt events in VS Code extension. Script works, config is correct, other hooks work, but Notification hooks are never called.

Detailed test report available if maintainers need it.

glasser · 8 months ago

I think duplicate of #11156

TimeHunter81 · 8 months ago
I think duplicate of #11156

Linked issue is a consequence if this one, which is the root cause. Once hooks are supported we can intercept notification events via hooks.
BTW, this is more a feature request for the VSCode extension than a bug. Unless the lack of feature parity with CLI is assumed to be a bug :)
Can't wait for this to be tackled by the Anthropic team so that I can share a Claude Code notifier that uplevels notifications to MacOS system notifs when Cursor / VSCode is in the background.

marcomulder · 8 months ago

+1 - This affects my workflow significantly.

My settings.json:

{
  "hooks": {
    "Notification": [{ "matcher": "", "hooks": [{ "type": "command", "command": "powershell.exe -Command \"[System.Media.SystemSounds]::Beep.Play()\"" }] }],
    "Stop": [{ "matcher": "", "hooks": [{ "type": "command", "command": "powershell.exe -Command \"[System.Media.SystemSounds]::Beep.Play()\"" }] }]
  }
}

Behavior:
✅ Stop hook works (beep when task completes)
❌ Notification hook does NOT fire when user input is needed
This works correctly in the terminal CLI. The VSCode extension should have feature parity.
lonedfx · 7 months ago

This is not macos specific, I'm on Windows and have the exact same issue.

thzking703 · 7 months ago

I'm experiencing the same issue. I analyzed the VSCode extension source code and found the root cause.

Environment

| Item | Value |
|------|-------|
| Host OS | Ubuntu Server (Linux 6.5.0-45-generic) |
| Claude Code CLI | 2.1.19 |
| Claude Code VSCode Extension | 2.1.19 |
| VSCode | 1.108.2 |
| Connection Method | VSCode Remote SSH from Mac/Windows |

Setup Details

[Local PC (Mac/Windows)]              [Ubuntu Server]
         VSCode  ──── SSH Connection ────→  Claude Code CLI (installed)
           │                                       ↑
           └── Remote SSH Extension ──→ Claude Code VSCode Extension
                                        (runs on server, invokes CLI)
  • Claude Code CLI is installed on the Ubuntu server
  • Local PC connects to Ubuntu server via VSCode Remote SSH extension
  • Claude Code VSCode extension runs on the server side and invokes the Claude Code CLI on the server
  • All hook scripts are executed on the Ubuntu server

Hook Behavior in My Environment

I have configured hooks for all available events:

| Hook | Terminal CLI | VSCode Extension |
|------|--------------|------------------|
| PreToolUse | Works | Works |
| PostToolUse | Works | Works |
| UserPromptSubmit | Works | Works |
| Stop | Works | Works |
| SubagentStop | Works | Works |
| SessionStart | Works | Works |
| SessionEnd | Works | Works |
| PreCompact | Works | Works |
| Notification | Works | Does NOT fire |
| PermissionRequest | Works | Does NOT fire |

---

Root Cause

Missing Subtype Handlers in processControlRequest()

The processControlRequest() method only handles 3 subtypes:

async processControlRequest(e,r){
  if(e.request.subtype==="can_use_tool"){
    // Implemented - handles tool permission requests
    return{...await this.canUseTool(...),toolUseID:e.request.tool_use_id}
  }else{
    if(e.request.subtype==="hook_callback"){
      // Implemented - handles PreToolUse/PostToolUse hooks
      return await this.handleHookCallbacks(e.request.callback_id,e.request.input,...)
    }
    if(e.request.subtype==="mcp_message"){
      // Implemented - handles MCP messages
    }
  }
  throw Error("Unsupported control request subtype: "+e.request.subtype)
  // Notification and PermissionRequest reach here and throw error
}

Subtype Support Matrix

| Subtype | Handler Method | Status |
|---------|---------------|--------|
| can_use_tool | canUseTool() | Implemented |
| hook_callback | handleHookCallbacks() | Implemented |
| mcp_message | MCP handler | Implemented |
| Notification | - | Not implemented |
| PermissionRequest | - | Not implemented |

Why PreToolUse/PostToolUse Work

These hooks are sent as hook_callback subtype and processed by handleHookCallbacks():

handleHookCallbacks(e,r,n,s){
  let i=this.hookCallbacks.get(e);
  if(!i)throw Error(`No hook callback found for ID: ${e}`);
  return i(r,n,{signal:s})  // Executes registered callback
}

Why Notification/PermissionRequest Don't Work

These are sent as separate subtypes (Notification, PermissionRequest) but there's no handler for them in processControlRequest(). They fall through to the final throw Error() statement.

Processing Flow Comparison

[PreToolUse/PostToolUse (works)]
CLI -> control_request(subtype="hook_callback") -> handleHookCallbacks() -> User hook executed

[Notification/PermissionRequest (doesn't work)]
CLI -> control_request(subtype="Notification") -> processControlRequest() -> No handler -> Error

---

Suggested Fix

Adding handlers for Notification and PermissionRequest subtypes in processControlRequest() should resolve this issue:

async processControlRequest(e,r){
  // ... existing handlers ...

  // Add these handlers:
  if(e.request.subtype==="Notification"){
    return await this.handleHookCallbacks(/* Notification hook params */);
  }
  if(e.request.subtype==="PermissionRequest"){
    return await this.handleHookCallbacks(/* PermissionRequest hook params */);
  }

  throw Error("Unsupported control request subtype: "+e.request.subtype)
}

Analyzed File

~/.vscode-server/extensions/anthropic.claude-code-2.1.19-linux-x64/extension.js

yurukusa · 5 months ago

Great root cause analysis by @thzking703. Until Anthropic patches the VSCode extension, here are workarounds for getting notifications in Native UI mode:
Workaround 1 — Use a different hook event as a proxy:
Since PreToolUse and PostToolUse fire correctly in the extension, you can use them to trigger notifications when Claude is likely waiting for input:

// settings.json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "*",
        "hook": "bash -c 'INPUT=$(cat); TOOL=$(echo \"$INPUT\" | jq -r .tool_name); if echo \"$INPUT\" | jq -e .tool_output | grep -q \"permission denied\\|blocked\\|error\" 2>/dev/null; then notify-send \"Claude needs attention\" \"Tool $TOOL encountered an issue\" 2>/dev/null || powershell.exe -Command \"[System.Media.SystemSounds]::Exclamation.Play()\" 2>/dev/null; fi'"
      }
    ]
  }
}

Workaround 2 — Run in CLI mode for critical sessions:
If notifications are essential (e.g., long-running autonomous tasks), running Claude Code in the integrated terminal (Ctrl+\claude`) instead of Native UI mode gives you full hook support including Notification and PermissionRequest.
Workaround 3 — Poll-based notification (heavier but reliable):
A SessionStart hook can launch a background watcher that monitors Claude's output for pause indicators:

{
  "hooks": {
    "SessionStart": [
      {
        "hook": "bash -c 'nohup bash -c \"while true; do sleep 30; if pgrep -f claude-code | xargs -I{} ls /proc/{}/fd 2>/dev/null | wc -l | grep -q '^[0-3]$'; then notify-send \\\"Claude may be idle\\\" 2>/dev/null; fi; done\" &'"
      }
    ]
  }
}

The proper fix is what @thzking703 described — adding Notification and PermissionRequest subtype handlers in processControlRequest(). But until that ships, these workarounds should keep you unblocked.

mayerwin · 4 months ago

This regression was one of the triggers that pushed me to write an extension instead of relying on the Notification hook. Rather than the single Notification event, mine listens to PermissionRequest / Stop / UserPromptSubmit / PostToolUse together and also tails the session transcript, so if any one path goes flaky in a new release, the others still catch it.

Sound for permission requests is separate from the completion sound, cross-platform, free: https://marketplace.visualstudio.com/items?itemName=mayerwin.ai-agent-sound-notification. Repo: https://github.com/mayerwin/AI-Agent-Sound-Notification. If your afplay-based setup still isn't firing after an update, this might be a quicker path than debugging the regression.

saiso · 4 months ago

+1 — affecting me too on VS Code 1.117 / Claude Code 2.1.121 / macOS 26.4.

A user-side priority note: I work primarily in the VS Code extension and only use the terminal CLI occasionally. The Notification hook used to fire in the extension before — this is a regression that broke an established workflow, not a missing feature.

If extension vs terminal triage has to be prioritized, I'd vote the extension first. For me, missing the audio cue in the extension means losing prompts during long tasks, while I personally don't need it in the terminal.

Thanks for the great work on Claude Code.

TedRubber · 4 months ago

Same issue here. I am still running 2.1.45 just to keep the notifications in VS Code.

shohei-ihaya · 3 months ago

+1 2.1.138 on macos

Showing cached comments. Read the full discussion on GitHub ↗