Skip to content

Commit e59d283

Browse files
feat(client): allow overriding retry count header (#536)
1 parent 02d0754 commit e59d283

File tree

2 files changed

+73
-3
lines changed

2 files changed

+73
-3
lines changed

src/core.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,11 @@ export abstract class APIClient {
363363
delete reqHeaders['content-type'];
364364
}
365365

366-
reqHeaders['x-stainless-retry-count'] = String(retryCount);
366+
// Don't set the retry count header if it was already set or removed by the caller. We check `headers`,
367+
// which can contain nulls, instead of `reqHeaders` to account for the removal case.
368+
if (getHeader(headers, 'x-stainless-retry-count') === undefined) {
369+
reqHeaders['x-stainless-retry-count'] = String(retryCount);
370+
}
367371

368372
this.validateHeaders(reqHeaders, headers);
369373

@@ -1144,7 +1148,15 @@ export const isHeadersProtocol = (headers: any): headers is HeadersProtocol => {
11441148
return typeof headers?.get === 'function';
11451149
};
11461150

1147-
export const getRequiredHeader = (headers: HeadersLike, header: string): string => {
1151+
export const getRequiredHeader = (headers: HeadersLike | Headers, header: string): string => {
1152+
const foundHeader = getHeader(headers, header);
1153+
if (foundHeader === undefined) {
1154+
throw new Error(`Could not find ${header} header`);
1155+
}
1156+
return foundHeader;
1157+
};
1158+
1159+
export const getHeader = (headers: HeadersLike | Headers, header: string): string | undefined => {
11481160
const lowerCasedHeader = header.toLowerCase();
11491161
if (isHeadersProtocol(headers)) {
11501162
// to deal with the case where the header looks like Stainless-Event-Id
@@ -1170,7 +1182,7 @@ export const getRequiredHeader = (headers: HeadersLike, header: string): string
11701182
}
11711183
}
11721184

1173-
throw new Error(`Could not find ${header} header`);
1185+
return undefined;
11741186
};
11751187

11761188
/**

tests/index.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,64 @@ describe('retries', () => {
272272
expect(count).toEqual(3);
273273
});
274274

275+
test('omit retry count header', async () => {
276+
let count = 0;
277+
let capturedRequest: RequestInit | undefined;
278+
const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
279+
count++;
280+
if (count <= 2) {
281+
return new Response(undefined, {
282+
status: 429,
283+
headers: {
284+
'Retry-After': '0.1',
285+
},
286+
});
287+
}
288+
capturedRequest = init;
289+
return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
290+
};
291+
const client = new Anthropic({ apiKey: 'my-anthropic-api-key', fetch: testFetch, maxRetries: 4 });
292+
293+
expect(
294+
await client.request({
295+
path: '/foo',
296+
method: 'get',
297+
headers: { 'X-Stainless-Retry-Count': null },
298+
}),
299+
).toEqual({ a: 1 });
300+
301+
expect(capturedRequest!.headers as Headers).not.toHaveProperty('x-stainless-retry-count');
302+
});
303+
304+
test('overwrite retry count header', async () => {
305+
let count = 0;
306+
let capturedRequest: RequestInit | undefined;
307+
const testFetch = async (url: RequestInfo, init: RequestInit = {}): Promise<Response> => {
308+
count++;
309+
if (count <= 2) {
310+
return new Response(undefined, {
311+
status: 429,
312+
headers: {
313+
'Retry-After': '0.1',
314+
},
315+
});
316+
}
317+
capturedRequest = init;
318+
return new Response(JSON.stringify({ a: 1 }), { headers: { 'Content-Type': 'application/json' } });
319+
};
320+
const client = new Anthropic({ apiKey: 'my-anthropic-api-key', fetch: testFetch, maxRetries: 4 });
321+
322+
expect(
323+
await client.request({
324+
path: '/foo',
325+
method: 'get',
326+
headers: { 'X-Stainless-Retry-Count': '42' },
327+
}),
328+
).toEqual({ a: 1 });
329+
330+
expect((capturedRequest!.headers as Headers)['x-stainless-retry-count']).toBe('42');
331+
});
332+
275333
test('retry on 429 with retry-after', async () => {
276334
let count = 0;
277335
const testFetch = async (url: RequestInfo, { signal }: RequestInit = {}): Promise<Response> => {

0 commit comments

Comments
 (0)