Surface network errors with proxies (microsoft/vscode#298236)

This commit is contained in:
Christof Marti
2026-04-08 09:18:44 +02:00
parent 801ef1046f
commit 43f7117748
10 changed files with 24 additions and 13 deletions

View File

@@ -552,7 +552,7 @@ class SingleFetcherService implements IFetcherService {
return this._fetcher.fetch(url, options);
}
createWebSocket(url: string, options?: WebSocketConnectOptions): WebSocketConnection {
return { webSocket: new WebSocket(url, options), responseHeaders: new HeadersImpl({}), responseStatusCode: undefined, responseStatusText: undefined };
return { webSocket: new WebSocket(url, options), responseHeaders: new HeadersImpl({}), responseStatusCode: undefined, responseStatusText: undefined, networkError: undefined };
}
disconnectAll(): Promise<unknown> {
return this._fetcher.disconnectAll();

View File

@@ -187,6 +187,7 @@ export interface WebSocketConnection {
readonly responseHeaders: IHeaders;
readonly responseStatusCode: number | undefined;
readonly responseStatusText: string | undefined;
readonly networkError: Error | undefined;
}
export interface IAbortSignal {

View File

@@ -369,8 +369,10 @@ class ChatWebSocketConnection extends Disposable implements IChatWebSocketConnec
this._responseStatusCode = connection.responseStatusCode;
this._responseStatusText = connection.responseStatusText;
const errorMessage = event.error ? `${event.message}: ${collectSingleLineErrorMessage(event.error)}` : event.message || 'WebSocket error';
const networkError = event.error?.cause ?? connection.networkError;
const networkErrorMessage = networkError ? collectSingleLineErrorMessage(networkError) : undefined;
const connectDurationMs = Date.now() - (this._connectStartTime ?? Date.now());
this._logService.error(`[ChatWebSocketManager] Connection error for conversation ${this._conversationId}: ${errorMessage}`);
this._logService.error(`[ChatWebSocketManager] Connection error for conversation ${this._conversationId}: ${errorMessage}${networkErrorMessage ? ` (cause: ${networkErrorMessage})` : ''}`);
ChatWebSocketTelemetrySender.sendConnectErrorTelemetry(this._telemetryService, {
conversationId: this._conversationId,
requestId: this.requestId,
@@ -379,6 +381,7 @@ class ChatWebSocketConnection extends Disposable implements IChatWebSocketConnec
connectDurationMs,
responseStatusCode: this._responseStatusCode,
responseStatusText: this._responseStatusText,
networkError: networkErrorMessage,
});
reject(new Error(errorMessage));
};

View File

@@ -26,6 +26,7 @@ export interface IChatWebSocketConnectErrorTelemetryProperties extends IChatWebS
connectDurationMs: number;
responseStatusCode: number | undefined;
responseStatusText: string | undefined;
networkError: string | undefined;
}
export interface IChatWebSocketCloseTelemetryProperties extends IChatWebSocketRequestTelemetryProperties {
@@ -141,7 +142,8 @@ export class ChatWebSocketTelemetrySender {
"error": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Error message for the failed connection" },
"connectDurationMs": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "Time until the connection error in milliseconds", "isMeasurement": true },
"responseStatusCode": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "HTTP response status code from the failed connection attempt", "isMeasurement": true },
"responseStatusText": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "HTTP response status text from the failed connection attempt" }
"responseStatusText": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "HTTP response status text from the failed connection attempt" },
"networkError": { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth", "comment": "The underlying network error code and message from the dispatch layer" }
}
*/
telemetryService.sendTelemetryErrorEvent('websocket.connectError', { github: true, microsoft: true }, {
@@ -150,6 +152,7 @@ export class ChatWebSocketTelemetrySender {
gitHubRequestId: properties.gitHubRequestId,
error: properties.error,
responseStatusText: properties.responseStatusText,
networkError: properties.networkError,
}, {
connectDurationMs: properties.connectDurationMs,
responseStatusCode: properties.responseStatusCode,

View File

@@ -93,6 +93,9 @@ export function createWebSocket(url: string, options?: WebSocketConnectOptions):
},
get responseStatusText() {
return (webSocket as { responseStatusText?: string }).responseStatusText ?? responseStatusText;
},
get networkError() {
return (webSocket as { networkError?: Error }).networkError ?? undefined;
}
};
}

View File

@@ -51,6 +51,7 @@ function createFakeCAPIClientService(ws: FakeWebSocket): ICAPIClientService {
responseHeaders: new HeadersImpl({}),
responseStatusCode: 101,
responseStatusText: 'Switching Protocols',
networkError: undefined,
} satisfies WebSocketConnection as unknown as WebSocketConnection),
} as unknown as ICAPIClientService;
}

8
package-lock.json generated
View File

@@ -27,7 +27,7 @@
"@vscode/iconv-lite-umd": "0.7.1",
"@vscode/native-watchdog": "^1.4.6",
"@vscode/policy-watcher": "^1.3.2",
"@vscode/proxy-agent": "^0.40.0",
"@vscode/proxy-agent": "^0.41.0",
"@vscode/ripgrep": "^1.17.1",
"@vscode/spdlog": "^0.15.8",
"@vscode/sqlite3": "5.1.12-vscode",
@@ -3863,9 +3863,9 @@
}
},
"node_modules/@vscode/proxy-agent": {
"version": "0.40.0",
"resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.40.0.tgz",
"integrity": "sha512-G2OUy5b2vxYXoRWo38BwxBKW1GCjwno9tivcshJNBWkeHjwcidLkL6KFaVRgIDDxJjojPkoxy9AivTDU/ksJ6g==",
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.41.0.tgz",
"integrity": "sha512-xdjSPUu6DyC7+RBRftrj06OBG/xVLc0dsxhhwMzwfd9/pOGm8j4Zc70arq1jQb0s7EF4m9dAFoNjmSigfzN25A==",
"license": "MIT",
"dependencies": {
"@tootallnate/once": "^3.0.0",

View File

@@ -104,7 +104,7 @@
"@vscode/iconv-lite-umd": "0.7.1",
"@vscode/native-watchdog": "^1.4.6",
"@vscode/policy-watcher": "^1.3.2",
"@vscode/proxy-agent": "^0.40.0",
"@vscode/proxy-agent": "^0.41.0",
"@vscode/ripgrep": "^1.17.1",
"@vscode/spdlog": "^0.15.8",
"@vscode/sqlite3": "5.1.12-vscode",

View File

@@ -17,7 +17,7 @@
"@vscode/deviceid": "^0.1.1",
"@vscode/iconv-lite-umd": "0.7.1",
"@vscode/native-watchdog": "^1.4.6",
"@vscode/proxy-agent": "^0.40.0",
"@vscode/proxy-agent": "^0.41.0",
"@vscode/ripgrep": "^1.17.1",
"@vscode/spdlog": "^0.15.8",
"@vscode/sqlite3": "5.1.12-vscode",
@@ -622,9 +622,9 @@
"license": "MIT"
},
"node_modules/@vscode/proxy-agent": {
"version": "0.40.0",
"resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.40.0.tgz",
"integrity": "sha512-G2OUy5b2vxYXoRWo38BwxBKW1GCjwno9tivcshJNBWkeHjwcidLkL6KFaVRgIDDxJjojPkoxy9AivTDU/ksJ6g==",
"version": "0.41.0",
"resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.41.0.tgz",
"integrity": "sha512-xdjSPUu6DyC7+RBRftrj06OBG/xVLc0dsxhhwMzwfd9/pOGm8j4Zc70arq1jQb0s7EF4m9dAFoNjmSigfzN25A==",
"license": "MIT",
"dependencies": {
"@tootallnate/once": "^3.0.0",

View File

@@ -12,7 +12,7 @@
"@vscode/deviceid": "^0.1.1",
"@vscode/iconv-lite-umd": "0.7.1",
"@vscode/native-watchdog": "^1.4.6",
"@vscode/proxy-agent": "^0.40.0",
"@vscode/proxy-agent": "^0.41.0",
"@vscode/ripgrep": "^1.17.1",
"@vscode/spdlog": "^0.15.8",
"@vscode/sqlite3": "5.1.12-vscode",