mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-08-04 18:28:30 +08:00
Add shouldRetry predicate to RetryOptions so callers can abort retries for errors that will never succeed (e.g. 401 WorkflowValidationSkipError). Previously, retryWithBackoff retried all errors blindly, wasting ~35s on deterministic failures like workflow validation 401s. Fixes #1081 Co-authored-by: Claude <noreply@anthropic.com>
48 lines
1.3 KiB
TypeScript
48 lines
1.3 KiB
TypeScript
export type RetryOptions = {
|
|
maxAttempts?: number;
|
|
initialDelayMs?: number;
|
|
maxDelayMs?: number;
|
|
backoffFactor?: number;
|
|
shouldRetry?: (error: Error) => boolean;
|
|
};
|
|
|
|
export async function retryWithBackoff<T>(
|
|
operation: () => Promise<T>,
|
|
options: RetryOptions = {},
|
|
): Promise<T> {
|
|
const {
|
|
maxAttempts = 3,
|
|
initialDelayMs = 5000,
|
|
maxDelayMs = 20000,
|
|
backoffFactor = 2,
|
|
shouldRetry,
|
|
} = options;
|
|
|
|
let delayMs = initialDelayMs;
|
|
let lastError: Error | undefined;
|
|
|
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
try {
|
|
console.log(`Attempt ${attempt} of ${maxAttempts}...`);
|
|
return await operation();
|
|
} catch (error) {
|
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
console.error(`Attempt ${attempt} failed:`, lastError.message);
|
|
|
|
if (shouldRetry && !shouldRetry(lastError)) {
|
|
console.error("Error is not retryable, giving up immediately");
|
|
throw lastError;
|
|
}
|
|
|
|
if (attempt < maxAttempts) {
|
|
console.log(`Retrying in ${delayMs / 1000} seconds...`);
|
|
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
delayMs = Math.min(delayMs * backoffFactor, maxDelayMs);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.error(`Operation failed after ${maxAttempts} attempts`);
|
|
throw lastError;
|
|
}
|