MCPcopy Create free account
hub / github.com/github/gh-aw / withRetry

Function withRetry

actions/setup/js/error_recovery.cjs:167–239  ·  view source on GitHub ↗

* Execute an operation with retry logic and exponential backoff * @template T * @param {() => Promise } operation - The async operation to execute * @param {Partial } [config] - Retry configuration (optional) * @param {string} [operationName] - Name of the operation for logging *

(operation, config = {}, operationName = "operation")

Source from the content-addressed store, hash-verified

165 * @throws {Error} If all retry attempts fail
166 */
167async function withRetry(operation, config = {}, operationName = "operation") {
168 const fullConfig = { ...DEFAULT_RETRY_CONFIG, ...config };
169 let lastError;
170 let delay = fullConfig.initialDelayMs;
171
172 for (let attempt = 0; attempt <= fullConfig.maxRetries; attempt++) {
173 try {
174 if (attempt > 0) {
175 const jitter = fullConfig.jitterMs > 0 ? Math.floor(Math.random() * fullConfig.jitterMs) : 0;
176 const delayWithJitter = delay + jitter;
177 core.info(`Retry attempt ${attempt}/${fullConfig.maxRetries} for ${operationName} after ${delayWithJitter}ms delay`);
178 logRetryEvent(lastError, operationName, attempt, delayWithJitter);
179 await sleep(delayWithJitter);
180 }
181
182 const result = await operation();
183
184 if (attempt > 0) {
185 core.info(`✓ ${operationName} succeeded on retry attempt ${attempt}`);
186 }
187
188 return result;
189 } catch (error) {
190 lastError = error;
191 const errorMsg = getErrorMessage(error);
192
193 // Check if this error should be retried
194 if (!fullConfig.shouldRetry(error)) {
195 core.debug(`${operationName} failed with non-retryable error: ${errorMsg}`);
196 throw enhanceError(error, {
197 operation: operationName,
198 attempt: attempt + 1,
199 retryable: false,
200 suggestion: "This error cannot be resolved by retrying. Please check the error details and fix the underlying issue.",
201 });
202 }
203
204 // If this was the last attempt, throw the enhanced error
205 if (attempt === fullConfig.maxRetries) {
206 core.warning(`${operationName} failed after ${fullConfig.maxRetries} retry attempts: ${errorMsg}`);
207 throw enhanceError(error, {
208 operation: operationName,
209 attempt: attempt + 1,
210 maxRetries: fullConfig.maxRetries,
211 retryable: true,
212 suggestion: "All retry attempts exhausted. This may be a persistent issue. Check GitHub status or try again later.",
213 });
214 }
215
216 // Log the retry attempt
217 core.warning(`${operationName} failed (attempt ${attempt + 1}/${fullConfig.maxRetries + 1}): ${errorMsg}`);
218
219 // Calculate next delay: honour Retry-After header when present, otherwise
220 // use exponential backoff. Either way the result is capped at maxDelayMs.
221 const retryAfterMs = getRetryAfterMs(error);
222 if (retryAfterMs !== null) {
223 const cappedDelay = Math.min(retryAfterMs, fullConfig.maxDelayMs);
224 if (cappedDelay < retryAfterMs) {

Callers 15

mainFunction · 0.85
updatePullRequestBranchFunction · 0.85
replace_label.cjsFile · 0.85
createReviewWithRetryFunction · 0.85
mainFunction · 0.85
createFallbackIssueFunction · 0.85
mainFunction · 0.85

Calls 5

logRetryEventFunction · 0.85
enhanceErrorFunction · 0.85
getRetryAfterMsFunction · 0.85
sleepFunction · 0.70
getErrorMessageFunction · 0.70

Tested by 1