Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/core/src/errors/sdkErrors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
9 changes: 8 additions & 1 deletion packages/core/src/shared/protocol.ts
Original file line number Diff line number Diff line change
Expand Up @@ -907,7 +907,14 @@ export abstract class Protocol<ContextT extends BaseContext> {
.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);
};

Expand Down
18 changes: 18 additions & 0 deletions packages/core/test/shared/protocol.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading