{"record":{"id":"88a3ed9b81cc7b82","repo":"hcengineering/platform","slug":"network-error-err-88a3ed","errorCode":null,"errorMessage":"Network error ${err}","messagePattern":"Network error (.+?)","errorType":"exception","errorClass":"NetworkError","httpStatus":null,"severity":"error","filePath":"packages/billing-client/src/client.ts","lineNumber":118,"sourceCode":"\n    await fetchSafe(url, { method: 'POST', headers: { ...this.headers }, body })\n  }\n\n  async postAiTokensData (data: AiTokensData[]): Promise<void> {\n    const path = '/api/v1/ai/tokens'\n    const url = new URL(concatLink(this.endpoint, path))\n    const body = JSON.stringify(data)\n\n    await fetchSafe(url, { method: 'POST', headers: { ...this.headers }, body })\n  }\n}\n\nasync function fetchSafe (url: string | URL, init?: RequestInit): Promise<Response> {\n  let response\n  try {\n    response = await fetch(url, init)\n  } catch (err: any) {\n    throw new NetworkError(`Network error ${err}`)\n  }\n\n  if (!response.ok) {\n    const text = await response.text()\n    throw new BillingError(text)\n  }\n\n  return response\n}\n","sourceCodeStart":100,"sourceCodeEnd":128,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/packages/billing-client/src/client.ts#L100-L128","documentation":"fetchSafe wraps the global fetch call; when fetch itself rejects — DNS failure, connection refused/reset, TLS error, request aborted — it rethrows as a NetworkError with the original error interpolated into the message. Non-2xx HTTP responses are NOT this error; those become BillingError instead.","triggerScenarios":"Any BillingClient method that goes through fetchSafe (response, postLiveKitSessions, postLiveKitEgress, postAiTranscriptData, postAiTokensData) when the billing service is unreachable, the URL hostname is wrong, the port is closed, or TLS fails.","commonSituations":"Billing service down or redeploying; wrong billing URL/port in config; DNS not resolving in cluster/k8s; firewall or network policy blocking egress; self-signed certificate issues.","solutions":["Verify the billing service is up and reachable: curl the billingUrl from the same host/container.","Check the configured billing URL, port, and protocol (http vs https, DNS name).","Inspect the inner error in the message (ENOTFOUND, ECONNREFUSED, certificate) to identify root cause.","Add retry with backoff for transient outages around client calls."],"exampleFix":"// before\nconst stats = await client.response(...) // Network error fetch failed\n// after\nconst url = new URL(process.env.BILLING_URL!)\nawait dns.promises.lookup(url.hostname) // verify reachability first\nconst stats = await client.response(...)","handlingStrategy":"retry","validationCode":"const url = new URL(billingUrl)\nawait dns.promises.lookup(url.hostname) // fail fast on unresolvable host before calling the client","typeGuard":"function isNetworkError(e: unknown): e is NetworkError {\n  return e instanceof NetworkError\n}","tryCatchPattern":"try {\n  return await client.response(ws)\n} catch (e) {\n  if (e instanceof NetworkError) {\n    await backoff(); return await client.response(ws) // retry transient connectivity failures\n  }\n  throw e\n}","preventionTips":["Health-check the billing endpoint before dependent calls","Configure retries with exponential backoff for transient network faults","Confirm billing URL/DNS/egress rules in each deployment environment","Distinguish NetworkError from BillingError when handling"],"tags":["network","fetch","connectivity"],"backgroundTag":"network-request-failed","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}