Agent Host client tool hangs after active client removal; browser click invoked with empty args

Open
#322,990 3 comments 0 reactions 1 assignee View on GitHub

@connor4312 is already working on this.

Since Jun 25, 2026.

Assessment

This issue has not been assessed yet.

Description

agent-host bug

Summary

A browser clickElement tool call can get stuck in Preparing after the Agent Host removes the active client. The click never reaches Playwright; the workbench invokes the browser tool with {} parameters and crashes during tool preparation.

Observed

In an Agents/Copilot CLI session using built-in browser tools:

  1. openBrowserPage succeeds.
  2. clickElement on the initial Start button succeeds.
  3. screenshotPage succeeds.
  4. The next clickElement on the board's Run button stays stuck at Preparing.

The session event stream ends with:

tool.execution_start clickElement
permission.requested clickElement
# no permission.completed
# no tool.execution_complete

Key log evidence

The model requested valid arguments:

{
  "element": "Run button on Research Agent",
  "pageId": "<page-id>",
  "ref": "e200"
}

But the workbench invoked the underlying browser tool with empty parameters:

[AgentHost] Invoking client tool: clickElement
[LanguageModelToolsService#invokeTool] Invoking tool click_element with parameters {}
[error] Cannot read properties of undefined (reading 'toString')
    at createBrowserPageLink(...)
    at ClickBrowserTool.prepareToolInvocation(...)

The AHP sequence for the same tool call was:

chat/toolCallStart clickElement
chat/toolCallReady confirmed="not-needed"           # no toolInput
chat/toolCallComplete success=false error="Client <client-id> disconnected before completing clickElement"
chat/toolCallReady confirmation UI with real toolInput

About 6 seconds earlier, the same session received:

session/activeClientRemoved clientId=<client-id>

Suspected cause

This looks like an Agent Host/client-tool lifecycle race:

  • The active client is removed from session state while the UI/client connection is still participating in the turn.
  • A later client-contributed browser tool call is treated as orphaned/disconnected.
  • The synthetic toolCallReady without toolInput is consumed by the workbench client-tool handler.
  • The handler falls back to {} and invokes click_element before the later toolCallReady containing the real toolInput arrives.
  • ClickBrowserTool.prepareToolInvocation assumes pageId is present, calls createBrowserPageLink(undefined), throws, and the original Copilot tool execution remains stuck waiting for completion/permission state.

Relevant code paths:

  • src/vs/platform/agentHost/node/protocolServerHandler.ts
    • disconnected client tool call handling emits synthetic ready/complete events
  • src/vs/workbench/contrib/chat/browser/agentSessions/agentHost/agentHostSessionHandler.ts
    • missing toolInput falls back to {} once status is not Streaming
  • src/vs/workbench/contrib/browserView/electron-browser/tools/clickBrowserTool.ts
    • prepareToolInvocation assumes pageId is defined

Expected

A disconnected/orphaned client-tool call should fail cleanly and unblock the model/UI, or be retried with a replacement active client. It should not invoke the local browser tool with {} parameters or leave the tool call stuck in Preparing.

Possible fixes

  • Do not invoke client tools from toolCallReady events that lack toolInput when the tool has required arguments or _meta.toolArguments exists.
  • Ensure disconnected-client synthetic toolCallReady/toolCallComplete ordering cannot race with later confirmation UI events for the same tool call.
  • Harden browser tool prepareToolInvocation against missing pageId so malformed protocol state becomes a clean tool error.
  • Investigate why session/activeClientRemoved fired while the client was still successfully confirming/completing tool calls in the same session.
Dominant language
TypeScript
Stars
193k
Forks
42.9k
PR merge metrics
PR metrics pending

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from microsoft/vscode

All issues in microsoft/vscode

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.