Skip to content

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

Description

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.

Metadata

Metadata

Labels

agent-hostIssues for the agent-host implementation (not the protocol)bugIssue identified by VS Code Team member as probable bug

Type

No type

Projects

Status
No status

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions