{"record":{"id":"89b5e946590f9477","repo":"hcengineering/platform","slug":"payment-service-error-response-status-text","errorCode":null,"errorMessage":"Payment service error: ${response.status} ${text}","messagePattern":"Payment service error: (.+?) (.+?)","errorType":"http","errorClass":"PaymentError","httpStatus":null,"severity":"error","filePath":"packages/payment-client/src/client.ts","lineNumber":170,"sourceCode":" * @returns Response\n * @throws NetworkError on network issues\n * @throws PaymentError on non-ok responses\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: ${String(err)}`)\n  }\n\n  if (!response.ok) {\n    const text = await response.text()\n    try {\n      const error = JSON.parse(text)\n      throw new PaymentError(error.error ?? text)\n    } catch {\n      throw new PaymentError(`Payment service error: ${response.status} ${text}`)\n    }\n  }\n\n  return response\n}\n","sourceCodeStart":152,"sourceCodeEnd":176,"githubUrl":"https://github.com/hcengineering/platform/blob/63e28dc96483967b2fc21c881b3f1023c1de7718/packages/payment-client/src/client.ts#L152-L176","documentation":"fetchSafe throws `Payment service error: ${response.status} ${text}` when the response is non-OK and its body does NOT parse as valid JSON (the JSON-parse attempt threw, so the catch branch runs). It bundles the HTTP status code and the raw body text into a PaymentError so the caller can still see what went wrong. This is the unstructured-response counterpart of the structured PaymentError path.","triggerScenarios":"Any PaymentClient call (via `response` → fetchSafe) receiving 4xx/5xx with an HTML/plain-text/empty body — e.g. an nginx 502 Bad Gateway page or a bare 'Internal Server Error' string.","commonSituations":"Payment service behind a reverse proxy that failed to reach the upstream (502/504), service crash returning HTML error page, load balancer maintenance page, or request blocked by a WAF returning HTML.","solutions":["Inspect the status code and raw text in the message — HTML bodies usually indicate a proxy/gateway problem rather than the payment app itself.","Check whether the payment service is up and whether its reverse proxy/load balancer is healthy.","Verify the baseUrl isn't pointing at the wrong host (e.g. a proxy instead of the API).","Retry with backoff if the status is 5xx, and alert if it persists."],"exampleFix":"// before\nconst res = await paymentClient.response('/charge', init)\n// after\ntry {\n  const res = await paymentClient.response('/charge', init)\n} catch (e) {\n  if (e instanceof PaymentError && /Payment service error: 5\\d\\d/.test(e.message)) {\n    // gateway/server outage — retry with backoff\n  } else throw e\n}","handlingStrategy":"try-catch","validationCode":"// pre-flight: ensure the service returns JSON (not a proxy page) before real calls\nconst health = await fetch(baseUrl + '/health')\nconst ct = health.headers.get('content-type') ?? ''\nif (!ct.includes('application/json')) throw new Error('Payment endpoint not serving JSON — check proxy/baseUrl')","typeGuard":"function isGatewayStyleError(e: unknown): boolean {\n  return e instanceof PaymentError && /Payment service error: (502|503|504)\\b/.test(e.message)\n}","tryCatchPattern":"try {\n  await paymentClient.response('/charge', init)\n} catch (e) {\n  if (isGatewayStyleError(e)) {\n    // 5xx with non-JSON body: proxy/gateway outage — retry with backoff or alert ops\n  } else throw e\n}","preventionTips":["Point baseUrl directly at the payment API, not a generic proxy that may return HTML pages.","Monitor gateway/upstream health (502/504 rates) with alerting.","Confirm TLS and host headers are correct so the WAF/LB doesn't intercept requests.","Treat non-JSON 5xx as infrastructure issues and page ops rather than requeuing user transactions silently."],"tags":["http","payment-error","gateway","server-error"],"backgroundTag":"http-5xx-gateway-error","analyzedSha":"63e28dc96483967b2fc21c881b3f1023c1de7718","analyzedAt":"2026-08-29T15:21:27.377Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}