Skip to content

Commit 462bcda

Browse files
feat(package): export a root error type (#338)
1 parent fd2337b commit 462bcda

File tree

4 files changed

+27
-16
lines changed

4 files changed

+27
-16
lines changed

src/core.ts

+15-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { VERSION } from './version';
22
import { Stream } from './streaming';
3-
import { APIError, APIConnectionError, APIConnectionTimeoutError, APIUserAbortError } from './error';
3+
import {
4+
OpenAIError,
5+
APIError,
6+
APIConnectionError,
7+
APIConnectionTimeoutError,
8+
APIUserAbortError,
9+
} from './error';
410
import {
511
kind as shimsKind,
612
type Readable,
@@ -440,7 +446,7 @@ export abstract class APIClient {
440446
if (value === null) {
441447
return `${encodeURIComponent(key)}=`;
442448
}
443-
throw new Error(
449+
throw new OpenAIError(
444450
`Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`,
445451
);
446452
})
@@ -599,7 +605,7 @@ export abstract class AbstractPage<Item> implements AsyncIterable<Item> {
599605
async getNextPage(): Promise<this> {
600606
const nextInfo = this.nextPageInfo();
601607
if (!nextInfo) {
602-
throw new Error(
608+
throw new OpenAIError(
603609
'No next page expected; please check `.hasNextPage()` before calling `.getNextPage()`.',
604610
);
605611
}
@@ -925,10 +931,10 @@ export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve
925931

926932
const validatePositiveInteger = (name: string, n: unknown): number => {
927933
if (typeof n !== 'number' || !Number.isInteger(n)) {
928-
throw new Error(`${name} must be an integer`);
934+
throw new OpenAIError(`${name} must be an integer`);
929935
}
930936
if (n < 0) {
931-
throw new Error(`${name} must be a positive integer`);
937+
throw new OpenAIError(`${name} must be a positive integer`);
932938
}
933939
return n;
934940
};
@@ -939,7 +945,7 @@ export const castToError = (err: any): Error => {
939945
};
940946

941947
export const ensurePresent = <T>(value: T | null | undefined): T => {
942-
if (value == null) throw new Error(`Expected a value to be given but received ${value} instead.`);
948+
if (value == null) throw new OpenAIError(`Expected a value to be given but received ${value} instead.`);
943949
return value;
944950
};
945951

@@ -962,14 +968,14 @@ export const coerceInteger = (value: unknown): number => {
962968
if (typeof value === 'number') return Math.round(value);
963969
if (typeof value === 'string') return parseInt(value, 10);
964970

965-
throw new Error(`Could not coerce ${value} (type: ${typeof value}) into a number`);
971+
throw new OpenAIError(`Could not coerce ${value} (type: ${typeof value}) into a number`);
966972
};
967973

968974
export const coerceFloat = (value: unknown): number => {
969975
if (typeof value === 'number') return value;
970976
if (typeof value === 'string') return parseFloat(value);
971977

972-
throw new Error(`Could not coerce ${value} (type: ${typeof value}) into a number`);
978+
throw new OpenAIError(`Could not coerce ${value} (type: ${typeof value}) into a number`);
973979
};
974980

975981
export const coerceBoolean = (value: unknown): boolean => {
@@ -1073,5 +1079,5 @@ export const toBase64 = (str: string | null | undefined): string => {
10731079
return btoa(str);
10741080
}
10751081

1076-
throw new Error('Cannot generate b64 string; Expected `Buffer` or `btoa` to be defined');
1082+
throw new OpenAIError('Cannot generate b64 string; Expected `Buffer` or `btoa` to be defined');
10771083
};

src/error.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
import { castToError, Headers } from './core';
44

5-
export class APIError extends Error {
5+
export class OpenAIError extends Error {}
6+
7+
export class APIError extends OpenAIError {
68
readonly status: number | undefined;
79
readonly headers: Headers | undefined;
810
readonly error: Object | undefined;

src/index.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export class OpenAI extends Core.APIClient {
103103
...opts
104104
}: ClientOptions = {}) {
105105
if (apiKey === undefined) {
106-
throw new Error(
106+
throw new Errors.OpenAIError(
107107
"The OPENAI_API_KEY environment variable is missing or empty; either provide it, or instantiate the OpenAI client with an apiKey option, like new OpenAI({ apiKey: 'my apiKey' }).",
108108
);
109109
}
@@ -116,7 +116,7 @@ export class OpenAI extends Core.APIClient {
116116
};
117117

118118
if (!options.dangerouslyAllowBrowser && Core.isRunningInBrowser()) {
119-
throw new Error(
119+
throw new Errors.OpenAIError(
120120
"It looks like you're running in a browser-like environment.\n\nThis is disabled by default, as it risks exposing your secret API credentials to attackers.\nIf you understand the risks and have appropriate mitigations in place,\nyou can set the `dangerouslyAllowBrowser` option to `true`, e.g.,\n\nnew OpenAI({ apiKey, dangerouslyAllowBrowser: true });\n\nhttps://help.openai.com/en/articles/5112595-best-practices-for-api-key-safety\n",
121121
);
122122
}
@@ -164,6 +164,7 @@ export class OpenAI extends Core.APIClient {
164164

165165
static OpenAI = this;
166166

167+
static OpenAIError = Errors.OpenAIError;
167168
static APIError = Errors.APIError;
168169
static APIConnectionError = Errors.APIConnectionError;
169170
static APIConnectionTimeoutError = Errors.APIConnectionTimeoutError;
@@ -179,6 +180,7 @@ export class OpenAI extends Core.APIClient {
179180
}
180181

181182
export const {
183+
OpenAIError,
182184
APIError,
183185
APIConnectionError,
184186
APIConnectionTimeoutError,

src/streaming.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { type Response } from './_shims/index';
2+
import { OpenAIError } from './error';
23

34
type Bytes = string | ArrayBuffer | Uint8Array | Buffer | null | undefined;
45

@@ -23,7 +24,7 @@ export class Stream<Item> implements AsyncIterable<Item> {
2324
private async *iterMessages(): AsyncGenerator<ServerSentEvent, void, unknown> {
2425
if (!this.response.body) {
2526
this.controller.abort();
26-
throw new Error(`Attempted to iterate over a response with no body`);
27+
throw new OpenAIError(`Attempted to iterate over a response with no body`);
2728
}
2829
const lineDecoder = new LineDecoder();
2930

@@ -198,7 +199,7 @@ class LineDecoder {
198199
return Buffer.from(bytes).toString();
199200
}
200201

201-
throw new Error(
202+
throw new OpenAIError(
202203
`Unexpected: received non-Uint8Array (${bytes.constructor.name}) stream chunk in an environment with a global "Buffer" defined, which this library assumes to be Node. Please report this error.`,
203204
);
204205
}
@@ -210,14 +211,14 @@ class LineDecoder {
210211
return this.textDecoder.decode(bytes);
211212
}
212213

213-
throw new Error(
214+
throw new OpenAIError(
214215
`Unexpected: received non-Uint8Array/ArrayBuffer (${
215216
(bytes as any).constructor.name
216217
}) in a web platform. Please report this error.`,
217218
);
218219
}
219220

220-
throw new Error(
221+
throw new OpenAIError(
221222
`Unexpected: neither Buffer nor TextDecoder are available as globals. Please report this error.`,
222223
);
223224
}

0 commit comments

Comments
 (0)