hcengineering/platform · error · NetworkError

Network error: ${String(err)}

Error message

Network error: ${String(err)}

What it means

fetchSafe wraps the underlying fetch call and converts low-level network failures into a typed NetworkError with the message `Network error: ${String(err)}`. This happens when fetch itself rejects — DNS failure, connection refused, TLS errors, aborted requests — before any HTTP response exists. The original error text is embedded in the message for diagnosis.

Source

Thrown at packages/payment-client/src/client.ts:161

    const response = await fetchSafe(url, { headers: { ...this.headers } })
    return (await response.json()) as CheckoutStatus
  }
}

/**
 * Safe fetch wrapper that handles errors consistently
 * @param url - URL to fetch
 * @param init - Fetch options
 * @returns Response
 * @throws NetworkError on network issues
 * @throws PaymentError on non-ok responses
 */
async function fetchSafe (url: string | URL, init?: RequestInit): Promise<Response> {
  let response
  try {
    response = await fetch(url, init)
  } catch (err: any) {
    throw new NetworkError(`Network error: ${String(err)}`)
  }

  if (!response.ok) {
    const text = await response.text()
    try {
      const error = JSON.parse(text)
      throw new PaymentError(error.error ?? text)
    } catch {
      throw new PaymentError(`Payment service error: ${response.status} ${text}`)
    }
  }

  return response
}

View on GitHub (pinned to 63e28dc964)

Solutions

  1. Read the embedded cause in the message (e.g. 'fetch failed', 'ENOTFOUND', 'ECONNREFUSED') to identify the network problem.
  2. Verify the payment service URL is correct and the service is running/reachable (curl the health endpoint).
  3. Check network connectivity, DNS, and egress/firewall rules from the client environment.
  4. Implement retry with backoff for transient outages; NetworkError is the type to match for retry decisions.

Example fix

// before
const res = await paymentClient.response('/charge', init)
// after
try {
  const res = await paymentClient.response('/charge', init)
} catch (e) {
  if (e instanceof NetworkError) {
    console.error('Payment service unreachable:', e.message)
    // retry or alert
  } else throw e
}
Defensive patterns

Strategy: retry

Validate before calling

const url = new URL(baseUrl) // throws immediately on malformed URL, before any request
if (!/^https?:$/.test(url.protocol)) throw new Error('Payment URL must be http(s)')

Type guard

function isNetworkError(e: unknown): e is NetworkError {
  return e instanceof NetworkError
}

Try / catch

async function withRetry<T>(fn: () => Promise<T>, attempts = 3): Promise<T> {
  for (let i = 1; ; i++) {
    try { return await fn() }
    catch (e) {
      if (isNetworkError(e) && i < attempts) {
        await new Promise(r => setTimeout(r, 2 ** i * 250)); continue
      }
      throw e
    }
  }
}

Prevention

When it happens

Trigger: Any PaymentClient operation (via `response`, which calls fetchSafe) where `fetch` throws: unreachable host, wrong port, DNS resolution failure, self-signed/expired certificate, offline environment, or request aborted.

Common situations: Payment service down or redeploying, typo in the payment URL hostname, container networking/DNS issues in Kubernetes, firewall blocking egress, or a browser CORS preflight hard-failing.

Related errors


AI-assisted analysis of hcengineering/platform@63e28dc964 (2026-08-29). Data as JSON: /api/errors/9f00ddcc8b561d89. Report an issue: GitHub.