@@ -172,7 +172,7 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
172172 logFinalFailure ( errorInfo , this . config . maxNetworkRetries )
173173 // Final error message
174174 const finalError = new Error ( `Network request failed after ${ this . config . maxNetworkRetries } retries: ${ errorInfo . reason } ` )
175- finalError . code = error . code
175+ finalError . code = error && error . code
176176 finalError . originalError = error
177177 finalError . retryAttempts = attempt - 1
178178 return Promise . reject ( finalError )
@@ -181,6 +181,16 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
181181 const delay = calculateNetworkRetryDelay ( attempt )
182182 logRetryAttempt ( errorInfo , attempt , delay )
183183
184+ // Guard: retry failures (e.g. from nested retries) may not have config in some
185+ // environments. Reject with a catchable error instead of throwing TypeError.
186+ if ( ! error || ! error . config ) {
187+ const finalError = new Error ( `Network request failed after retries: ${ errorInfo . reason } ` )
188+ finalError . code = error && error . code
189+ finalError . originalError = error
190+ finalError . retryAttempts = attempt - 1
191+ return Promise . reject ( finalError )
192+ }
193+
184194 // Initialize retry count if not present
185195 if ( ! error . config . networkRetryCount ) {
186196 error . config . networkRetryCount = 0
@@ -200,9 +210,7 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
200210 safeAxiosRequest ( requestConfig )
201211 . then ( ( response ) => {
202212 // On successful retry, call the original onComplete to properly clean up
203- if ( error . config . onComplete ) {
204- error . config . onComplete ( )
205- }
213+ error ?. config ?. onComplete ?. ( )
206214 shift ( ) // Process next queued request
207215 resolve ( response )
208216 } )
@@ -214,17 +222,13 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
214222 . then ( resolve )
215223 . catch ( ( finalError ) => {
216224 // On final failure, clean up the running queue
217- if ( error . config . onComplete ) {
218- error . config . onComplete ( )
219- }
225+ error ?. config ?. onComplete ?. ( )
220226 shift ( ) // Process next queued request
221227 reject ( finalError )
222228 } )
223229 } else {
224230 // On non-retryable error, clean up the running queue
225- if ( error . config . onComplete ) {
226- error . config . onComplete ( )
227- }
231+ error ?. config ?. onComplete ?. ( )
228232 shift ( ) // Process next queued request
229233 reject ( retryError )
230234 }
@@ -429,9 +433,12 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
429433 }
430434 } )
431435 }
432- // Response interceptor used for
436+ // Response interceptor used for success and for error path (Promise.reject(responseHandler(err))).
437+ // When used with an error, err may lack config (e.g. plugin returns new error). Guard so we don't throw.
433438 const responseHandler = ( response ) => {
434- response . config . onComplete ( )
439+ if ( response ?. config ?. onComplete ) {
440+ response . config . onComplete ( )
441+ }
435442 shift ( )
436443 return response
437444 }
@@ -461,13 +468,27 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
461468 }
462469
463470 const responseErrorHandler = error => {
464- let networkError = error . config . retryCount
471+ // Guard: Axios errors normally have config; missing config can occur when a retry
472+ // fails in certain environments or when non-Axios errors propagate (e.g. timeouts).
473+ // Reject with a catchable error instead of throwing TypeError and crashing the process.
474+ if ( ! error || ! error . config ) {
475+ const fallbackError = new Error (
476+ error && typeof error . message === 'string'
477+ ? error . message
478+ : 'Network request failed: error object missing request config'
479+ )
480+ fallbackError . code = error ?. code
481+ fallbackError . originalError = error
482+ return Promise . reject ( runPluginOnResponseForError ( fallbackError ) )
483+ }
484+
485+ let networkError = error ?. config ?. retryCount ?? 0
465486 let retryErrorType = null
466487
467488 // First, check for transient network errors
468489 const networkErrorInfo = isTransientNetworkError ( error )
469490 if ( networkErrorInfo && this . config . retryOnNetworkFailure ) {
470- const networkRetryCount = error . config . networkRetryCount || 0
491+ const networkRetryCount = error ? .config ? .networkRetryCount || 0
471492 return retryNetworkError ( error , networkErrorInfo , networkRetryCount + 1 )
472493 }
473494
@@ -482,7 +503,7 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
482503 var response = error . response
483504 if ( ! response ) {
484505 if ( error . code === 'ECONNABORTED' ) {
485- const timeoutMs = error . config . timeout || this . config . timeout || 'unknown'
506+ const timeoutMs = error ? .config ? .timeout || this . config . timeout || 'unknown'
486507 error . response = {
487508 ...error . response ,
488509 status : 408 ,
0 commit comments