{"record":{"id":"05c4f6a28168ddec","repo":"antiwork/gumroad","slug":"something-went-wrong-05c4f6","errorCode":null,"errorMessage":"Something went wrong.","messagePattern":"Something went wrong\\.","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"critical","filePath":"app/javascript/data/customer_surcharge.ts","lineNumber":83,"sourceCode":"          tax_cents: number;\n          shipping_cents: number;\n          total_cents: number;\n        }[]\n      | undefined;\n  } | null;\n  detected_buyer_currency?: string | null | undefined;\n  available_buyer_currencies?: { code: string; label: string }[] | undefined;\n};\n\nexport const getSurcharges = async (data: GetSurchargesRequest, abortSignal?: AbortSignal) => {\n  const response = await request({\n    method: \"POST\",\n    accept: \"json\",\n    url: Routes.customer_surcharges_path(),\n    abortSignal,\n    data,\n  });\n  if (!response.ok) throw new ResponseError();\n  return typia.assert<SurchargesResponse>(await response.json());\n};\n","sourceCodeStart":65,"sourceCodeEnd":86,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/data/customer_surcharge.ts#L65-L86","documentation":"ResponseError (\"Something went wrong.\") thrown by getSurcharges when POST /customer_surcharge (CustomerSurchargeController#calculate_all) answers non-2xx. This runs in the buyer-facing purchase flow to compute currency surcharges, so a failure here degrades checkout. request() pre-converts 5xx/429/network/abort into other error types, so this throw is a 4xx: 422 for invalid currency/amount data, 404 for an unknown product, 401 for auth problems.","triggerScenarios":"Posting a detected_buyer_currency or available_buyer_currencies entry the server does not support (422); calculating surcharges for a product that was just unpublished or deleted (404); a session/auth problem on the purchase page (401); aborting is explicitly NOT this error — abortSignal produces AbortError instead.","commonSituations":"Checkout pages kept open while the product is unpublished; browser currency (from navigator or geolocation) supplying an unexpected currency code; stale checkout bundles after a deploy changed the surcharge params; extensions stripping request bodies.","solutions":["DevTools → Network: inspect the /customer_surcharge response body — a 422 names the invalid field.","Confirm the currency codes sent are uppercase ISO 4217 codes the server supports; log detected_buyer_currency when it fails.","404: verify the product/links involved are still published and purchasable.","Since this sits in the purchase flow, add a fallback in the UI (hide the surcharge line or block the buy button) rather than letting the error break checkout."],"exampleFix":"// before\nconst response = await request({ method: \"POST\", accept: \"json\", url: Routes.customer_surcharges_path(), abortSignal, data });\n\n// after — validate the payload shape the server charges on\nconst ISO_4217 = /^[A-Z]{3}$/;\nif (!data.currency_code || !ISO_4217.test(data.currency_code))\n  throw new Error(`Unsupported currency: ${String(data.currency_code)}`);\nconst response = await request({ method: \"POST\", accept: \"json\", url: Routes.customer_surcharges_path(), abortSignal, data });","handlingStrategy":"fallback","validationCode":"const ISO_4217 = /^[A-Z]{3}$/;\nconst surchargeRequestOk =\n  (data.currency_code === undefined || ISO_4217.test(data.currency_code)) &&\n  (data.detected_buyer_currency === null || data.detected_buyer_currency === undefined || ISO_4217.test(data.detected_buyer_currency)) &&\n  (data.available_buyer_currencies === undefined || data.available_buyer_currencies.every((c) => ISO_4217.test(c.code) && c.label.length > 0));\nif (!surchargeRequestOk) return; // skip the call rather than break checkout\nawait getSurcharges(data, abortSignal);","typeGuard":"const isSurchargesResponse = (v: unknown): v is SurchargesResponse =>\n  typeof v === \"object\" && v !== null && \"surcharges\" in v;","tryCatchPattern":"import { assertResponseError } from \"$app/utils/request\";\ntry {\n  const { surcharges } = await getSurcharges(data, abortSignal);\n} catch (e) {\n  if (e instanceof AbortError) return; // unmounts are not failures\n  assertResponseError(e);\n  hideSurchargeLine(); // keep checkout usable without the surcharge breakdown\n}","preventionTips":["Pass the abortSignal so navigating away during checkout does not log spurious failures.","Send uppercase ISO 4217 currency codes only; log detected_buyer_currency whenever this call fails.","Because this runs in the purchase flow, always pair it with a UI fallback — never let it disable the buy button silently."],"tags":["surcharge","checkout","currency","buyer-flow","http","fetch"],"backgroundTag":"http-4xx-response","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}