{"record":{"id":"2411ec36b567a6b5","repo":"antiwork/gumroad","slug":"responsedata-message","errorCode":null,"errorMessage":"${responseData.message}","messagePattern":"\\$\\{responseData\\.message\\}","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"warning","filePath":"app/javascript/components/CustomDomain.tsx","lineNumber":64,"sourceCode":"      timeout = setTimeout(() => {\n        setVerificationInfo((prevState) => ({ ...prevState, buttonState: \"initial\" }));\n      }, 2000);\n    }\n    return () => clearTimeout(timeout);\n  }, [verificationInfo.buttonState]);\n\n  const verifyCustomDomain = asyncVoid(async () => {\n    setVerificationInfo({ buttonState: \"verifying\", state: \"verifying\", message: \"\" });\n\n    try {\n      const response = await request({\n        method: \"POST\",\n        accept: \"json\",\n        url: Routes.custom_domain_verifications_path(),\n        data: { domain: customDomain, product_id: productId },\n      });\n      const responseData = typia.assert<{ success: boolean; message: string }>(await response.json());\n      if (!responseData.success) throw new ResponseError(responseData.message);\n      setVerificationInfo({ buttonState: \"success\", state: \"success\", message: responseData.message });\n    } catch (e) {\n      assertResponseError(e);\n      setVerificationInfo({ buttonState: \"failure\", state: \"failure\", message: e.message });\n    }\n  });\n\n  return (\n    <Fieldset\n      state={\n        verificationInfo.state === \"success\" ? \"success\" : verificationInfo.state === \"failure\" ? \"danger\" : undefined\n      }\n    >\n      <FieldsetTitle>\n        <Label htmlFor={uid}>{label}</Label>\n        {includeLearnMoreLink ? (\n          <a href=\"/help/article/153-setting-up-a-custom-domain\" target=\"_blank\" rel=\"noreferrer\">\n            Learn more","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/components/CustomDomain.tsx#L46-L82","documentation":"verifyCustomDomain POSTs the domain and product id to custom_domain_verifications_path and expects { success, message }. On success:false the server's message (DNS instructions, propagation status) is thrown as the ResponseError message and rendered into the fieldset's failure state. The catch's assertResponseError re-throws non-ResponseError shapes (e.g. TypiaError on an unexpected body), which would crash the component rather than show the failure state.","triggerScenarios":"The domain's CNAME/TXT records not yet propagated or pointing at the wrong target; domain already verified on another Gumroad account; apex domain submitted where a subdomain is required — each returns success:false with a message describing the DNS problem.","commonSituations":"Seller clicks Verify seconds after adding the record (TTL not elapsed); typo in the CNAME target; registrar defaulting to an A record; Cloudflare proxying (orange cloud) hiding the CNAME value from verification.","solutions":["Read the failure message — it is the server's diagnosis of the DNS state, usually naming the missing/wrong record.","Check the DNS yourself: dig CNAME subdomain.example.com (or the TXT record) against the value the setup instructions gave.","Wait out DNS TTL (minutes to hours) and verify again — propagation, not breakage, is the most common cause.","If the domain is on another account, it must be released there first.","Disable the registrar's proxy for the record during verification if it masks the target."],"exampleFix":"// before\n} catch (e) {\n  assertResponseError(e);\n  setVerificationInfo({ buttonState: 'failure', state: 'failure', message: e.message });\n}\n\n// after — a body-shape surprise should land in the failure state, not crash the component\n} catch (e) {\n  const message = e instanceof ResponseError ? e.message : 'Verification failed. Please try again.';\n  setVerificationInfo({ buttonState: 'failure', state: 'failure', message });\n}","handlingStrategy":"validation","validationCode":"const DOMAIN = /^(?!-)[a-z0-9-]{1,63}(?<!-)(\\.[a-z0-9-]{1,63})+$/i;\n\nif (!DOMAIN.test(customDomain.trim())) {\n  setVerificationInfo({ buttonState: 'failure', state: 'failure', message: 'Enter a valid domain, e.g. shop.example.com.' });\n  return;\n}","typeGuard":"const isVerificationResult = (v: unknown): v is { success: boolean; message: string } =>\n  typeof v === 'object' && v !== null &&\n  typeof (v as { success?: unknown }).success === 'boolean' &&\n  typeof (v as { message?: unknown }).message === 'string';","tryCatchPattern":"try {\n  await verifyCustomDomain();\n} catch (e) {\n  const message = e instanceof ResponseError ? e.message : 'Verification failed. Please try again.';\n  setVerificationInfo({ buttonState: 'failure', state: 'failure', message });\n}","preventionTips":["Validate domain syntax before POSTing — malformed input wastes a verification round-trip.","Show the DNS record values to set BEFORE offering the Verify button, so first attempts are not doomed by missing records.","Tell users DNS propagation takes minutes-to-hours; offer a re-check later instead of an error on the first click.","Catch non-ResponseError shapes into the failure state — an escaped TypiaError crashes the settings page.","Suggest disabling registrar proxying (e.g. the orange cloud) for the verification record."],"tags":["dns","custom-domain","verification","http","api-error-body"],"backgroundTag":"domain-dns-verification-failed","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}