{"record":{"id":"b6fee12420c44bf2","repo":"nodejs/node","slug":"request-failed","errorCode":null,"errorMessage":"Request failed","messagePattern":"Request failed","errorType":"exception","errorClass":"RequestRetryError","httpStatus":null,"severity":"error","filePath":"deps/undici/src/lib/handler/retry-handler.js","lineNumber":371,"sourceCode":"      // for instance not safe to assume if the response is byte-per-byte\n      // equal\n      if (\n        this.etag != null &&\n        this.etag[0] === 'W' &&\n        this.etag[1] === '/'\n      ) {\n        this.etag = null\n      }\n\n      this.headersSent = true\n      this.handler.onResponseStart?.(\n        this.controllerProxy,\n        statusCode,\n        headers,\n        statusMessage\n      )\n    } else {\n      throw new RequestRetryError('Request failed', statusCode, {\n        headers,\n        data: { count: this.retryCount }\n      })\n    }\n  }\n\n  onResponseData (_controller, chunk) {\n    if (this.error) {\n      return\n    }\n\n    this.start += chunk.length\n\n    this.handler.onResponseData?.(this.controllerProxy, chunk)\n  }\n\n  onResponseEnd (_controller, trailers) {\n    if (this.error && this.retryOpts.throwOnError) {","sourceCodeStart":353,"sourceCodeEnd":389,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/deps/undici/src/lib/handler/retry-handler.js#L353-L389","documentation":"Thrown by RetryHandler.onResponseStart in the else branch: a new response is starting, the handler already knows an end boundary (this.end != null, set from a prior 206/Content-Length) but headers have not been sent yet, and the code path fell through to the else. This indicates an internal state inconsistency in the retry/resume flow rather than a normal user error; the handler surfaces it as a RequestRetryError carrying the status code so the caller sees the failure instead of a silent stall.","triggerScenarios":"A retry attempt produces a fresh 2xx response after an end boundary was already computed from a previous attempt, in a combination of statuses the handler did not expect (e.g. an initial 206 set this.end, then a retry returned a 200 without going through the resume branch).","commonSituations":"Servers whose retry behavior is non-deterministic across attempts (206 then 200, or vice versa); intercepted/proxied responses that alter status codes; edge cases in CDN failover. Usually surfaces as 'Request failed' with the offending status code.","solutions":["Capture statusCode and headers from the error's data and verify the server returns consistent statuses across retries.","Reduce retry aggressiveness for the affected route (lower maxRetries, narrow statusCodes/errorCodes).","If reproducible, report it with the response sequence (status codes + headers) so the retry contract can be reconciled with the origin.","For endpoints that cannot be safely resumed, fetch atomically without the retry interceptor."],"exampleFix":"// before\nclient = client.compose(interceptors.retry({ maxRetries: 5 }))\n\n// after (constrain retry to idempotent, range-honoring responses)\nclient = client.compose(interceptors.retry({\n  maxRetries: 2,\n  statusCodes: [500, 502, 503, 504],\n  errorCodes: ['ECONNRESET', 'EPIPE']\n}))","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try { await retryClient.request(opts) } catch (e) { if (e.code === 'UND_ERR_REQ_RETRY' && e.message === 'Request failed') { const status = e.data?.statusCode ?? e.statusCode; /* narrow retry policy or fetch atomically */ await baseClient.request(opts) } else throw e }","preventionTips":["Constrain retryOpts (statusCodes/errorCodes/maxRetries) to deterministic cases.","Watch for origins returning inconsistent statuses across attempts.","Capture statusCode + headers from the error data to diagnose."],"tags":["undici","http","retry","internal","state-inconsistency"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}