@@ -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,7 +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 ) {
213+ if ( error . config && error . config . onComplete ) {
204214 error . config . onComplete ( )
205215 }
206216 shift ( ) // Process next queued request
@@ -214,15 +224,15 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
214224 . then ( resolve )
215225 . catch ( ( finalError ) => {
216226 // On final failure, clean up the running queue
217- if ( error . config . onComplete ) {
227+ if ( error . config && error . config . onComplete ) {
218228 error . config . onComplete ( )
219229 }
220230 shift ( ) // Process next queued request
221231 reject ( finalError )
222232 } )
223233 } else {
224234 // On non-retryable error, clean up the running queue
225- if ( error . config . onComplete ) {
235+ if ( error . config && error . config . onComplete ) {
226236 error . config . onComplete ( )
227237 }
228238 shift ( ) // Process next queued request
@@ -429,9 +439,12 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
429439 }
430440 } )
431441 }
432- // Response interceptor used for
442+ // Response interceptor used for success and for error path (Promise.reject(responseHandler(err))).
443+ // When used with an error, err may lack config (e.g. plugin returns new error). Guard so we don't throw.
433444 const responseHandler = ( response ) => {
434- response . config . onComplete ( )
445+ if ( response ?. config ?. onComplete ) {
446+ response . config . onComplete ( )
447+ }
435448 shift ( )
436449 return response
437450 }
@@ -461,13 +474,27 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
461474 }
462475
463476 const responseErrorHandler = error => {
464- let networkError = error . config . retryCount
477+ // Guard: Axios errors normally have config; missing config can occur when a retry
478+ // fails in certain environments or when non-Axios errors propagate (e.g. timeouts).
479+ // Reject with a catchable error instead of throwing TypeError and crashing the process.
480+ if ( ! error || ! error . config ) {
481+ const fallbackError = new Error (
482+ error && typeof error . message === 'string'
483+ ? error . message
484+ : 'Network request failed: error object missing request config'
485+ )
486+ if ( error && error . code ) fallbackError . code = error . code
487+ fallbackError . originalError = error
488+ return Promise . reject ( runPluginOnResponseForError ( fallbackError ) )
489+ }
490+
491+ let networkError = error ?. config ?. retryCount ?? 0
465492 let retryErrorType = null
466493
467494 // First, check for transient network errors
468495 const networkErrorInfo = isTransientNetworkError ( error )
469496 if ( networkErrorInfo && this . config . retryOnNetworkFailure ) {
470- const networkRetryCount = error . config . networkRetryCount || 0
497+ const networkRetryCount = error ? .config ? .networkRetryCount || 0
471498 return retryNetworkError ( error , networkErrorInfo , networkRetryCount + 1 )
472499 }
473500
@@ -482,7 +509,7 @@ export function ConcurrencyQueue ({ axios, config, plugins = [] }) {
482509 var response = error . response
483510 if ( ! response ) {
484511 if ( error . code === 'ECONNABORTED' ) {
485- const timeoutMs = error . config . timeout || this . config . timeout || 'unknown'
512+ const timeoutMs = error ? .config ? .timeout || this . config . timeout || 'unknown'
486513 error . response = {
487514 ...error . response ,
488515 status : 408 ,
0 commit comments