More small retry fixes

This commit is contained in:
Konstantin Wohlwend 2026-05-26 14:48:19 -07:00
parent 7dd764324a
commit 4854e551a8
2 changed files with 30 additions and 3 deletions

View File

@ -1,6 +1,6 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { KnownErrors } from "../known-errors";
import { InternalSession } from "../sessions";
import { InternalSession, RefreshToken } from "../sessions";
import { Result } from "../utils/results";
import { HexclaveClientInterface } from "./client-interface";
@ -498,6 +498,21 @@ describe("_withFallback", () => {
expect(urlIndex(urls, log[1])).toBe(1);
});
it("does not fall back on wrapped non-KnownError 4xx refresh token responses", async () => {
const urls = urlList(3);
const log: string[] = [];
vi.stubGlobal("fetch", vi.fn(async (input: RequestInfo | URL) => {
const url = input instanceof Request ? input.url : input.toString();
log.push(url);
return createTextResponse("Payments are not set up", { status: 402 });
}));
const iface = createClientInterface({ apiUrls: urls });
await expect(iface.fetchNewAccessToken(new RefreshToken("refresh-token"))).rejects.toThrow("Payments are not set up");
expect(log.length).toBe(1);
expect(urlIndex(urls, log[0])).toBe(0);
});
it("makes 2 passes × N URLs attempts before throwing", async () => {
for (const n of [2, 3, 5]) {
const urls = urlList(n);

View File

@ -248,8 +248,20 @@ export class HexclaveClientInterface {
}
private _isNonRetryableApiResponseError(error: unknown) {
const cause = error instanceof Error ? error.cause : undefined;
return cause instanceof Response && cause.status >= 400 && cause.status < 500;
const response = this._getApiResponseFromError(error);
return response != null && response.status >= 400 && response.status < 500;
}
private _getApiResponseFromError(error: unknown, seenErrors = new Set<Error>()): Response | null {
if (error instanceof Response) {
return error;
}
if (!(error instanceof Error) || seenErrors.has(error)) {
return null;
}
seenErrors.add(error);
return this._getApiResponseFromError(error.cause, seenErrors);
}
/**