[BUG] Plugin-registered LSP servers emit 'Async hook Stop completed' messages on every turn

Resolved 💬 3 comments Opened Mar 8, 2026 by JDProfresh Closed Apr 7, 2026

Preflight Checklist

  • [x] I have searched existing issues and this hasn't been reported yet
  • [x] I can reproduce this bug consistently

Bug Description

Plugin-registered LSP servers (those configured via .lsp.json in a plugin directory) emit Async hook Stop completed messages after every user turn. The number of messages matches the number of plugin-registered LSP servers running. Built-in LSP servers (TypeScript, Pyright) do not produce these messages.

Steps to Reproduce

  1. Install LSP servers that use the plugin .lsp.json registration mechanism. For example, create a local plugin with:

bash-language-server plugin (.lsp.json):

{
  "bash": {
    "command": "bash-language-server",
    "args": ["start"],
    "extensionToLanguage": {
      ".sh": "shellscript"
    }
  }
}

vscode-langservers plugin (.lsp.json):

{
  "html": {
    "command": "vscode-html-language-server",
    "args": ["--stdio"],
    "transport": "stdio",
    "extensionToLanguage": { ".html": "html" }
  },
  "css": {
    "command": "vscode-css-language-server",
    "args": ["--stdio"],
    "transport": "stdio",
    "extensionToLanguage": { ".css": "css" }
  }
}
  1. Start a Claude Code session in a project with .sh, .html, or .css files
  2. Send any message and receive a response
  3. Observe 3x Async hook Stop completed messages appearing (one per LSP server)

Expected Behavior

No Async hook Stop completed messages should appear from LSP server lifecycle events. Built-in LSPs (TypeScript, Pyright) correctly suppress these messages.

Actual Behavior

After every assistant response, 3 messages appear:

└ Async hook Stop completed
└ Async hook Stop completed
└ Async hook Stop completed

The count matches the number of plugin-registered LSP servers (bash, CSS, HTML = 3).

Key Observation

The official typescript-lsp and pyright-lsp plugins from claude-plugins-official are stub plugins (README-only, no .lsp.json). Their LSP support is built into Claude Code natively and does not produce these messages. Only LSP servers registered through the plugin .lsp.json mechanism trigger the spurious output.

This suggests the plugin-based LSP lifecycle management uses a different code path that doesn't suppress async hook completion messages the way the built-in LSP path does.

Related

  • #32082 requests suppression for Stop/SubagentStop hooks generally. This issue is specifically about LSP plugin lifecycle events leaking through as Stop hook messages.

Environment

  • Claude Code version: latest (2026-03-08)
  • Platform: Linux (WSL2)
  • Node: v24.2.0

Priority

Low -- cosmetic only, no functional impact. The messages are noise but don't affect LSP functionality.

View original on GitHub ↗

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