From 1389b4fac18f4fb6a3ff374c4d4203adb18e9b3a Mon Sep 17 00:00:00 2001 From: rocketraccoon Date: Mon, 10 Aug 2026 17:05:01 +0700 Subject: [PATCH] feat: add exp 429 retry --- packages/wdio-types/src/Options.ts | 5 +++++ packages/webdriver/src/constants.ts | 3 +++ packages/webdriver/src/request/index.ts | 13 ++++++++++++- packages/webdriver/src/request/types.ts | 1 + packages/webdriver/src/utils.ts | 2 +- 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/packages/wdio-types/src/Options.ts b/packages/wdio-types/src/Options.ts index 4daffd53084..94380615e40 100644 --- a/packages/wdio-types/src/Options.ts +++ b/packages/wdio-types/src/Options.ts @@ -141,6 +141,11 @@ export interface WebDriver extends Connection { * @default 3 */ connectionRetryCount?: number + /** + * Base delay in ms for exponential backoff on 429 responses during session creation. + * If set, retries use exponential backoff instead of immediate retry. + */ + exp429RetryBaseDelay?: number /** * Specify custom headers to pass into every request. */ diff --git a/packages/webdriver/src/constants.ts b/packages/webdriver/src/constants.ts index bfc92f71538..df06e231542 100644 --- a/packages/webdriver/src/constants.ts +++ b/packages/webdriver/src/constants.ts @@ -92,6 +92,9 @@ export const DEFAULTS: Options.Definition> = { type: 'number', default: 3 }, + exp429RetryBaseDelay: { + type: 'number' + }, /** * Override default agent */ diff --git a/packages/webdriver/src/request/index.ts b/packages/webdriver/src/request/index.ts index dd5bb292e1a..895def42ccb 100644 --- a/packages/webdriver/src/request/index.ts +++ b/packages/webdriver/src/request/index.ts @@ -75,6 +75,7 @@ export default abstract class WebDriverRequest extends EventEmitter { fullRequestOptions = options.transformRequest(fullRequestOptions) } + fullRequestOptions.exp429RetryBaseDelay = options.exp429RetryBaseDelay this.emit('request', fullRequestOptions) return this._request(fullRequestOptions, options.transformResponse, options.customWdRequestAgent, options.connectionRetryCount, 0) } @@ -178,7 +179,7 @@ export default abstract class WebDriverRequest extends EventEmitter { log.info('DATA', transformCommandLogResult(fullRequestOptions.json)) } - const { url, retry: _, ...requestLibOptions } = fullRequestOptions + const { url, retry: _, exp429RetryBaseDelay: __, ...requestLibOptions } = fullRequestOptions const startTime = this._libPerformanceNow() let response = customWdRequestAgent ? await customWdRequestAgent.request(url!, requestLibOptions).catch((err: RequestLibError) => err) @@ -207,6 +208,16 @@ export default abstract class WebDriverRequest extends EventEmitter { this.emit('performance', { request: fullRequestOptions, durationMillisecond, success: false, error, retryCount }) log.warn(msg) log.info(`Retrying ${retryCount}/${totalRetryCount}`) + + const exp429RetryBaseDelay = fullRequestOptions.exp429RetryBaseDelay + if (exp429RetryBaseDelay && !(response instanceof Error) && response.statusCode === 429) { + const delay = Math.round(exp429RetryBaseDelay * 2 ** (retryCount - 1) + Math.random() * 100) + log.warn(`Session creation failed with 429, retrying in ${delay}ms`) + return new Promise(resolve => setTimeout(resolve, delay)).then( + () => this._request(fullRequestOptions, transformResponse, customWdRequestAgent, totalRetryCount, retryCount) + ) + } + return this._request(fullRequestOptions, transformResponse, customWdRequestAgent, totalRetryCount, retryCount) } diff --git a/packages/webdriver/src/request/types.ts b/packages/webdriver/src/request/types.ts index 5a072b3d74a..33ab4beae48 100644 --- a/packages/webdriver/src/request/types.ts +++ b/packages/webdriver/src/request/types.ts @@ -53,4 +53,5 @@ export interface RequestLibOptions { username?: string password?: string body?: unknown + exp429RetryBaseDelay?: number } diff --git a/packages/webdriver/src/utils.ts b/packages/webdriver/src/utils.ts index 2494ed2b32a..5153acb69b9 100644 --- a/packages/webdriver/src/utils.ts +++ b/packages/webdriver/src/utils.ts @@ -95,7 +95,7 @@ export async function startWebDriverSession (params: RemoteConfig): Promise<{ se let response: SessionInitializationResponse try { - response = await sessionRequest.makeRequest(params) as SessionInitializationResponse + response = await sessionRequest.makeRequest({ ...params, exp429RetryBaseDelay: params.exp429RetryBaseDelay ?? 5000 }) as SessionInitializationResponse } catch (err) { log.error(err) const message = getSessionError(err as Error, params)