Skip to content

Commit 36eb60d

Browse files
authored
fix: silence abort error in http agent (#420)
* fix: silence abort error in http agent
1 parent a00e7cc commit 36eb60d

File tree

3 files changed

+53
-2
lines changed

3 files changed

+53
-2
lines changed

typescript-sdk/packages/client/src/run/http-request.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,13 @@ export const runHttpRequest = (url: string, requestInit: RequestInit): Observabl
7676
})();
7777

7878
return () => {
79-
reader.cancel();
79+
reader.cancel().catch((error) => {
80+
if ((error as DOMException)?.name === "AbortError") {
81+
return;
82+
}
83+
84+
throw error;
85+
});
8086
};
8187
});
8288
}),

typescript-sdk/packages/client/src/transform/__tests__/http.test.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,40 @@ describe("transformHttpEventStream", () => {
5454
expect(receivedEvents).toEqual([mockBaseEvent]);
5555
});
5656

57+
test("should emit RUN_ERROR and complete on AbortError without erroring", () => {
58+
const mockHttpSource = new Subject<HttpEvent>();
59+
const receivedEvents: BaseEvent[] = [];
60+
let completed = false;
61+
let receivedError: unknown = undefined;
62+
63+
const result$ = transformHttpEventStream(mockHttpSource);
64+
result$.subscribe({
65+
next: (event) => receivedEvents.push(event),
66+
error: (err) => {
67+
receivedError = err;
68+
},
69+
complete: () => {
70+
completed = true;
71+
},
72+
});
73+
74+
mockHttpSource.next({
75+
type: HttpEventType.HEADERS,
76+
status: 200,
77+
headers: new Headers([["content-type", "text/event-stream"]]),
78+
});
79+
80+
const abortError = { name: "AbortError" } as DOMException;
81+
mockHttpSource.error(abortError);
82+
83+
expect(receivedEvents).toHaveLength(1);
84+
expect(receivedEvents[0].type).toBe(EventType.RUN_ERROR);
85+
const runErrorEvent = receivedEvents[0] as any;
86+
expect(runErrorEvent.rawEvent).toBe(abortError);
87+
expect(completed).toBe(true);
88+
expect(receivedError).toBeUndefined();
89+
});
90+
5791
test("should handle parseProtoStream errors", (done) => {
5892
// Given
5993
const mockHttpSource = new Subject<HttpEvent>();

typescript-sdk/packages/client/src/transform/http.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { HttpEvent, HttpEventType } from "../run/http-request";
44
import { parseSSEStream } from "./sse";
55
import { parseProtoStream } from "./proto";
66
import * as proto from "@ag-ui/proto";
7+
import { EventType } from "@ag-ui/core";
78

89
/**
910
* Transforms HTTP events into BaseEvents using the appropriate format parser based on content type.
@@ -47,7 +48,17 @@ export const transformHttpEventStream = (source$: Observable<HttpEvent>): Observ
4748
eventSubject.error(err);
4849
}
4950
},
50-
error: (err) => eventSubject.error(err),
51+
error: (err) => {
52+
if ((err as DOMException)?.name === "AbortError") {
53+
eventSubject.next({
54+
type: EventType.RUN_ERROR,
55+
rawEvent: err,
56+
});
57+
eventSubject.complete();
58+
return;
59+
}
60+
return eventSubject.error(err)
61+
},
5162
complete: () => eventSubject.complete(),
5263
});
5364
}

0 commit comments

Comments
 (0)