Skip to content

Commit f39bc36

Browse files
stainless-botRobertCraigie
authored andcommitted
chore(client): handle expo fetch abort errors
chore: unknown commit message
1 parent 3393fe1 commit f39bc36

File tree

3 files changed

+19
-6
lines changed

3 files changed

+19
-6
lines changed

src/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import type { HTTPMethod, PromiseOrValue, MergedRequestInit } from './internal/t
55
import { uuid4 } from './internal/utils/uuid';
66
import { validatePositiveInteger, isAbsoluteURL } from './internal/utils/values';
77
import { sleep } from './internal/utils/sleep';
8-
import { castToError } from './internal/errors';
8+
import { castToError, isAbortError } from './internal/errors';
99
import type { APIResponseProps } from './internal/parse';
1010
import { getPlatformHeaders } from './internal/detect-platform';
1111
import * as Shims from './internal/shims';
@@ -483,7 +483,7 @@ export class OpenAI {
483483
if (retriesRemaining) {
484484
return this.retryRequest(options, retriesRemaining);
485485
}
486-
if (response.name === 'AbortError') {
486+
if (isAbortError(response)) {
487487
throw new Errors.APIConnectionTimeoutError();
488488
}
489489
throw new Errors.APIConnectionError({ cause: response });

src/internal/errors.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
export function isAbortError(err: unknown) {
44
return (
5-
(err instanceof Error && err.name === 'AbortError') ||
6-
(typeof err === 'object' && err && 'name' in err && (err as any).name === 'AbortError')
5+
typeof err === 'object' &&
6+
err !== null &&
7+
// Spec-compliant fetch implementations
8+
(('name' in err && (err as any).name === 'AbortError') ||
9+
// Expo fetch
10+
('message' in err && String((err as any).message).includes('FetchRequestCanceledException')))
711
);
812
}
913

src/streaming.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { type ReadableStream } from './internal/shim-types';
33
import { makeReadableStream } from './internal/shims';
44
import { LineDecoder } from './internal/decoders/line';
55
import { ReadableStreamToAsyncIterable } from './internal/shims';
6+
import { isAbortError } from './internal/errors';
67

78
import { APIError } from './error';
89

@@ -77,7 +78,7 @@ export class Stream<Item> implements AsyncIterable<Item> {
7778
done = true;
7879
} catch (e) {
7980
// If the user calls `stream.controller.abort()`, we should exit without throwing.
80-
if (e instanceof Error && e.name === 'AbortError') return;
81+
if (isAbortError(e)) return;
8182
throw e;
8283
} finally {
8384
// If the user `break`s, abort the ongoing request.
@@ -124,7 +125,7 @@ export class Stream<Item> implements AsyncIterable<Item> {
124125
done = true;
125126
} catch (e) {
126127
// If the user calls `stream.controller.abort()`, we should exit without throwing.
127-
if (e instanceof Error && e.name === 'AbortError') return;
128+
if (isAbortError(e)) return;
128129
throw e;
129130
} finally {
130131
// If the user `break`s, abort the ongoing request.
@@ -208,6 +209,14 @@ export async function* _iterSSEMessages(
208209
): AsyncGenerator<ServerSentEvent, void, unknown> {
209210
if (!response.body) {
210211
controller.abort();
212+
if (
213+
typeof (globalThis as any).navigator !== 'undefined' &&
214+
(globalThis as any).navigator.product === 'ReactNative'
215+
) {
216+
throw new OpenAIError(
217+
`The default react-native fetch implementation does not support streaming. Please use expo/fetch: https://docs.expo.dev/versions/latest/sdk/expo/#expofetch-api`,
218+
);
219+
}
211220
throw new OpenAIError(`Attempted to iterate over a response with no body`);
212221
}
213222

0 commit comments

Comments
 (0)