MCPcopy Index your code
hub / github.com/simstudioai/sim / retryWithExponentialBackoff

Function retryWithExponentialBackoff

apps/sim/lib/knowledge/documents/utils.ts:98–167  ·  view source on GitHub ↗
(
  operation: () => Promise<T>,
  options: RetryOptions = {}
)

Source from the content-addressed store, hash-verified

96 * Executes a function with exponential backoff retry logic
97 */
98export async function retryWithExponentialBackoff<T>(
99 operation: () => Promise<T>,
100 options: RetryOptions = {}
101): Promise<T> {
102 const {
103 maxRetries = 5,
104 initialDelayMs = 1000,
105 maxDelayMs = 30000,
106 backoffMultiplier = 2,
107 retryCondition = isRetryableError,
108 } = options
109
110 let lastError: Error | undefined
111 let delay = initialDelayMs
112
113 for (let attempt = 0; attempt <= maxRetries; attempt++) {
114 try {
115 logger.debug(`Executing operation attempt ${attempt + 1}/${maxRetries + 1}`)
116 const result = await operation()
117
118 if (attempt > 0) {
119 logger.info(`Operation succeeded after ${attempt + 1} attempts`)
120 }
121
122 return result
123 } catch (error) {
124 lastError = toError(error)
125 logger.warn(`Operation failed on attempt ${attempt + 1}`, { error })
126
127 // If this is the last attempt, throw the error
128 if (attempt === maxRetries) {
129 logger.error(`Operation failed after ${maxRetries + 1} attempts`, { error })
130 throw lastError
131 }
132
133 // Check if error is retryable
134 if (!retryCondition(error as RetryableError)) {
135 logger.warn('Error is not retryable, throwing immediately', { error })
136 throw lastError
137 }
138
139 // Use Retry-After if the server told us how long to wait, otherwise exponential backoff.
140 // Cap Retry-After at maxDelayMs to bound total retry duration (matches Google Cloud SDK behavior).
141 const retryAfterMs = (lastError as HTTPError)?.retryAfterMs
142 const cappedRetryAfter = retryAfterMs ? Math.min(retryAfterMs, maxDelayMs) : undefined
143
144 if (retryAfterMs && retryAfterMs > maxDelayMs) {
145 logger.warn(
146 `Retry-After ${retryAfterMs}ms exceeds maxDelayMs ${maxDelayMs}ms — capping to ${maxDelayMs}ms`
147 )
148 }
149
150 const jitter = randomFloat() * 0.1 * delay
151 const actualDelay = cappedRetryAfter ?? Math.min(delay + jitter, maxDelayMs)
152
153 logger.info(
154 `Retrying in ${Math.round(actualDelay)}ms (attempt ${attempt + 1}/${maxRetries + 1})${cappedRetryAfter ? ' (Retry-After)' : ''}`
155 )

Callers 6

rerankFunction · 0.90
callEmbeddingAPIFunction · 0.90
secureFetchWithRetryFunction · 0.90
parseWithAzureMistralOCRFunction · 0.90
executeMistralOCRRequestFunction · 0.90
fetchWithRetryFunction · 0.85

Calls 7

toErrorFunction · 0.90
randomFloatFunction · 0.90
sleepFunction · 0.90
debugMethod · 0.80
infoMethod · 0.80
errorMethod · 0.80
warnMethod · 0.65

Tested by

no test coverage detected