From 667b4bcdb9f45d7f3e0c80aaf8b7ff2a683fb3b7 Mon Sep 17 00:00:00 2001 From: sakthivel Date: Fri, 29 May 2026 12:51:20 +0530 Subject: [PATCH] fix(protocol): add RequestAborted error code for AbortSignal cancellation --- packages/core/src/errors/sdkErrors.ts | 2 ++ packages/core/src/shared/protocol.ts | 9 ++++++++- packages/core/test/shared/protocol.test.ts | 18 ++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/core/src/errors/sdkErrors.ts b/packages/core/src/errors/sdkErrors.ts index af432c6389..f7f606672a 100644 --- a/packages/core/src/errors/sdkErrors.ts +++ b/packages/core/src/errors/sdkErrors.ts @@ -22,6 +22,8 @@ export enum SdkErrorCode { // Transport errors /** Request timed out waiting for response */ RequestTimeout = 'REQUEST_TIMEOUT', + /** Request was aborted via AbortSignal */ + RequestAborted = 'REQUEST_ABORTED', /** Connection was closed */ ConnectionClosed = 'CONNECTION_CLOSED', /** Failed to send message */ diff --git a/packages/core/src/shared/protocol.ts b/packages/core/src/shared/protocol.ts index 361bd6fc7c..640a06d352 100644 --- a/packages/core/src/shared/protocol.ts +++ b/packages/core/src/shared/protocol.ts @@ -907,7 +907,14 @@ export abstract class Protocol { .catch(error => this._onerror(new Error(`Failed to send cancellation: ${error}`))); // Wrap the reason in an SdkError if it isn't already - const error = reason instanceof SdkError ? reason : new SdkError(SdkErrorCode.RequestTimeout, String(reason)); + let error: SdkError; + if (reason instanceof SdkError) { + error = reason; + } else if (reason instanceof DOMException && reason.name === 'AbortError') { + error = new SdkError(SdkErrorCode.RequestAborted, reason.message); + } else { + error = new SdkError(SdkErrorCode.RequestAborted, String(reason)); + } reject(error); }; diff --git a/packages/core/test/shared/protocol.test.ts b/packages/core/test/shared/protocol.test.ts index 619e09376a..ce1ca83cd2 100644 --- a/packages/core/test/shared/protocol.test.ts +++ b/packages/core/test/shared/protocol.test.ts @@ -209,6 +209,24 @@ describe('protocol tests', () => { } }); + test('should throw RequestAborted (not RequestTimeout) when aborted via AbortSignal', async () => { + await protocol.connect(transport); + const mockSchema: ZodType<{ result: string }> = z.object({ result: z.string() }); + const controller = new AbortController(); + + const requestPromise = testRequest(protocol, { method: 'example', params: {} }, mockSchema, { + signal: controller.signal, + timeout: 60_000 + }); + + controller.abort(new DOMException('User cancelled', 'AbortError')); + + const error = await requestPromise.catch((e: unknown) => e); + expect(error).toBeInstanceOf(SdkError); + expect((error as SdkError).code).toBe(SdkErrorCode.RequestAborted); + expect((error as SdkError).code).not.toBe(SdkErrorCode.RequestTimeout); + }); + test('should invoke onclose when the connection is closed', async () => { const oncloseMock = vi.fn(); protocol.onclose = oncloseMock;