microsoft / microsoft/vscode

Copilot agent request can hang when WebSocket emits `error` without `close`

Open
#331,003 0 comments 0 reactions 1 assignee Claimed by @chrmarti View on GitHub
Dominant language
TypeScript
Stars
193k
Forks
42.4k
PR merge metrics
PR metrics pending

Description

- Copilot Chat Extension Version: 0.61.0
- VS Code Version: 1.133.0
- OS Version: Windows 11 Enterprise, build 26200
- Feature: Agent mode
- Selected model: Not captured in the sanitized occurrence record
- Logs: Sanitized signature and state transition below; no prompts or responses included

## Steps to Reproduce

1. Start a long-running Copilot agent request using a model whose metadata enables the Responses API WebSocket endpoint.
2. While an active request is awaiting `handle.firstEvent` / `handle.done`, cause the Electron WebSocket to emit an `error` event without a subsequent `close` event. The linked PR's unit test reproduces this deterministically with `FakeWebSocket.simulateError()` and no close.
3. Observe that the agent remains pending until the user presses Stop or Retry.

## Expected

An active-socket error should settle the request immediately. The existing request-error path can then retry over HTTP when eligible, or show a terminal error to the user.

## Actual

`ChatWebSocketConnection` records the error in `_pendingErrorMessage`, but only calls `handleConnectionClose` from the socket `close` listener. If no close event follows, both `firstEvent` and `done` remain pending, so the request has no terminal transition and HTTP fallback never starts.

Observed log signature:

```text
[ChatWebSocketManager] Error for conversation turn : :
```

No matching later line was present for the same conversation and turn:

```text
[ChatWebSocketManager] Connection closed for conversation turn ...
```

After manual intervention, persisted state showed:

```text
native status: idle (1)
lastResponseState: 2
transport failure: chat-websocket-error
```

This occurred in five conversations in one extension host on the same day. One affected conversation reproduced the signature again while the investigation was running. Tool executions had completed; the silent interval began after a tool/sub-turn, not inside a terminal command.

## Root Cause

In `extensions/copilot/src/platform/networking/node/chatWebSocketManager.ts`, the active-socket `error` handler only stores `_pendingErrorMessage`. Request settlement occurs in the independent `close` handler. The implementation assumes every error is followed by close, which is not guaranteed by the observed Electron transport behavior.

## Proposed Fix

On an active-socket error:

1. Mark the connection closed.
2. Clear the active request from the connection.
3. Settle it through `handleConnectionClose(1006, '', errorMessage)`.
4. Close the underlying socket.

The linked PR's regression emits `error` without `close` and verifies `firstEvent` and `done` reject and the connection closes.

## Validation

- Test-only patch at `2e0b8aad459cc615790f9510ea8fe480ae034333`: the new regression timed out after 5000 ms, confirming that the request remained pending.
- Implementation patch applied: all 24 tests in `chatWebSocketManager.spec.ts` passed.
- `npm run typecheck`: passed.
- `npm run lint`: passed with zero warnings.
- TypeScript language diagnostics and `git diff --check`: clean.

Potentially related but not duplicate: #314336 and #329550. Neither public report identifies the error-without-close state-machine path.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.