diff --git a/src/core/functions/uploads/createSignedUploadURL.ts b/src/core/functions/uploads/createSignedUploadURL.ts index 3077517..19da059 100644 --- a/src/core/functions/uploads/createSignedUploadURL.ts +++ b/src/core/functions/uploads/createSignedUploadURL.ts @@ -84,49 +84,79 @@ export const createSignedUploadURL = async ( }; } - try { - const request = await fetch(`${endpoint}/files/sign`, { - method: "POST", - headers: headers, - cache: "no-store", - body: JSON.stringify(payload), - }); - - if (!request.ok) { - const errorData = await request.text(); - if (request.status === 401 || request.status === 403) { - throw new AuthenticationError( - `Authentication Failed: ${errorData}`, - request.status, - { - error: errorData, - code: "AUTH_ERROR", - metadata: { - requestUrl: request.url, + const maxRetries = 3; + + for (let attempt = 0; attempt <= maxRetries; attempt++) { + try { + const request = await fetch(`${endpoint}/files/sign`, { + method: "POST", + headers: headers, + cache: "no-store", + body: JSON.stringify(payload), + }); + + if (!request.ok) { + const errorData = await request.text(); + if (request.status === 401 || request.status === 403) { + throw new AuthenticationError( + `Authentication Failed: ${errorData}`, + request.status, + { + error: errorData, + code: "AUTH_ERROR", + metadata: { + requestUrl: request.url, + }, }, + ); + } + throw new NetworkError(`HTTP error: ${errorData}`, request.status, { + error: errorData, + code: "HTTP_ERROR", + metadata: { + requestUrl: request.url, }, + }); + } + + const res = await request.json(); + return res.data; + } catch (error) { + // Don't retry auth errors or client errors (except 429) + if (error instanceof AuthenticationError) { + throw error; + } + if ( + error instanceof NetworkError && + error.statusCode && + error.statusCode >= 400 && + error.statusCode < 500 && + error.statusCode !== 429 + ) { + throw error; + } + + // If we've exhausted retries, throw the error + if (attempt === maxRetries) { + if (error instanceof PinataError) { + throw error; + } + if (error instanceof Error) { + throw new PinataError( + `Error processing createSignedURL: ${error.message}`, + ); + } + throw new PinataError( + "An unknown error occurred while getting signed url", ); } - throw new NetworkError(`HTTP error: ${errorData}`, request.status, { - error: errorData, - code: "HTTP_ERROR", - metadata: { - requestUrl: request.url, - }, - }); - } - const res = await request.json(); - return res.data; - } catch (error) { - if (error instanceof PinataError) { - throw error; + // Wait before retrying (exponential backoff: 1s, 2s, 4s) + const delay = Math.min(1000 * Math.pow(2, attempt), 4000); + await new Promise((resolve) => setTimeout(resolve, delay)); } - if (error instanceof Error) { - throw new PinataError( - `Error processing createSignedURL: ${error.message}`, - ); - } - throw new PinataError("An unknown error occurred while getting signed url"); } + + // This should never be reached, but TypeScript requires it + throw new PinataError("An unknown error occurred while getting signed url"); };