{"record":{"id":"a3bf27fe9cb21d73","repo":"PaddlePaddle/PaddleOCR","slug":"request-timed-out-after-timeoutms-ms","errorCode":null,"errorMessage":"Request timed out after ${timeoutMs}ms","messagePattern":"Request timed out after (.+?)ms","errorType":"exception","errorClass":"RequestTimeoutError","httpStatus":null,"severity":"error","filePath":"api_sdk/typescript/src/internal/http.ts","lineNumber":218,"sourceCode":"    const abort = () => abortController.abort();\n    timeoutController.signal.addEventListener(\"abort\", abort, { once: true });\n    if (signal?.aborted) {\n      abort();\n    } else {\n      signal?.addEventListener(\"abort\", abort, { once: true });\n    }\n    try {\n      resp = await this.fetchImpl(url, {\n        ...init,\n        headers,\n        signal: abortController.signal,\n      });\n    } catch (e: unknown) {\n      if (signal?.aborted) {\n        throw userAbortReason(signal);\n      }\n      if (timeoutController.signal.aborted) {\n        throw new RequestTimeoutError(effectiveTimeout, { cause: e });\n      }\n      const message = e instanceof Error ? e.message : String(e);\n      throw new NetworkError(`Connection failed: ${message}`);\n    } finally {\n      clearTimeout(timeoutID);\n      signal?.removeEventListener(\"abort\", abort);\n    }\n\n    if (resp.ok) return resp;\n\n    let text = await resp.text();\n    try {\n      const payload = JSON.parse(text) as { msg?: string; message?: string; errorMsg?: string };\n      text = payload.msg || payload.message || payload.errorMsg || text;\n    } catch {\n      // Keep raw response text.\n    }\n    if (resp.status === 401 || resp.status === 403) {","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/PaddlePaddle/PaddleOCR/blob/2661c7c0ef5c613e8f93c6e93b2e052399f0f854/api_sdk/typescript/src/internal/http.ts#L200-L236","documentation":"RequestTimeoutError is thrown by the low-level fetch() when the underlying fetch rejects because the per-request timeout controller aborted — the request did not complete within effectiveTimeout milliseconds. The configured timeout is exposed on `timeoutMs` and the original network error on `cause`. It fires only when the user-supplied AbortSignal did not abort first (that case rethrows the user's abort reason instead).","triggerScenarios":"Any HTTP call where the server or network is slower than the effective timeout: uploading a very large file to submitFile on a slow link, polling a status endpoint behind a slow proxy, or a default timeout that is too small for big payloads. The poller passes its remaining wait budget as timeoutMs, so long-poll loops near their deadline also hit this.","commonSituations":"Default request timeout too low for 50+ MB PDF uploads; high-latency regions far from the API endpoint; mobile/flaky networks; cold-start backends that take seconds to respond; containers with constrained bandwidth.","solutions":["Pass a larger timeout: submitFile(model, path, payload, { timeoutMs: 300_000 }) or raise the SDK default","Reduce payload size (downscale images, trim page ranges) so the upload fits the existing budget","Retry transient slow requests — the class is designed to be retryable with backoff","Check network path (proxy, VPN, DNS) if every request times out, not just large ones"],"exampleFix":"// before\nconst jobId = await client.submitFile(model, bigPdf, {});\n\n// after\nconst jobId = await client.submitFile(model, bigPdf, {}, { timeoutMs: 5 * 60_000 });\n// or for polling:\nconst result = await poller.waitForResult(jobId, { maxWaitTime: 10 * 60_000 });","handlingStrategy":"retry","validationCode":"function sizeNeedsBiggerTimeout(bytes: number, bandwidthBps: number, timeoutMs: number): boolean {\n  return (bytes * 8) / (bandwidthBps) * 1000 > timeoutMs * 0.8;\n}","typeGuard":"function isRequestTimeout(e: unknown): e is RequestTimeoutError {\n  return e instanceof RequestTimeoutError;\n}","tryCatchPattern":"try {\n  await client.submitFile(model, filePath, payload, { timeoutMs: 300_000 });\n} catch (e) {\n  if (e instanceof RequestTimeoutError) {\n    if (e.timeoutMs >= 300_000) throw new Error(\"Upload too slow — reduce file size\");\n    return client.submitFile(model, filePath, payload, { timeoutMs: e.timeoutMs * 2 });\n  }\n  throw e;\n}","preventionTips":["Scale timeoutMs with payload size (rough rule: at least bytes/bandwidth plus slack)","Pass generous timeouts for uploads and result downloads; keep tight ones only for cheap status calls","Retry on RequestTimeoutError with doubled budget before assuming failure"],"tags":["network","timeout","http","typescript"],"backgroundTag":null,"analyzedSha":"2661c7c0ef5c613e8f93c6e93b2e052399f0f854","analyzedAt":"2026-08-14T20:17:30.180Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}