{"record":{"id":"96abf2d860b0a98e","repo":"antiwork/gumroad","slug":"sorry-something-went-wrong-please-try-again-96abf2","errorCode":null,"errorMessage":"Sorry, something went wrong. Please try again.","messagePattern":"Sorry, something went wrong\\. Please try again\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"app/javascript/components/EmailConfirmationBanner.tsx","lineNumber":31,"sourceCode":"\ntype EmailConfirmationBannerProps = EmailConfirmation & {\n  children?: React.ReactNode;\n};\n\n// Deliberately not dismissible: account confirmation gates real product actions, so the\n// reminder stays up until the seller actually confirms.\nexport const EmailConfirmationBanner = ({ email, can_resend, children }: EmailConfirmationBannerProps) => {\n  const [resendState, setResendState] = React.useState<\"initial\" | \"sending\" | \"sent\">(\"initial\");\n\n  const resendConfirmationEmail = async () => {\n    setResendState(\"sending\");\n    try {\n      const response = await request({\n        method: \"POST\",\n        url: Routes.resend_confirmation_email_settings_main_path(),\n        accept: \"json\",\n      });\n      if (!response.ok) throw new Error();\n      // The endpoint replies 200 with { success: false } when the resend was\n      // rejected — the only rejection case is that there is nothing left to\n      // confirm (e.g. the email was confirmed in another tab while the banner\n      // was still up), so tell the seller that instead of a generic error.\n      const responseData = typia.assert<{ success: boolean }>(await response.json());\n      if (!responseData.success) {\n        setResendState(\"initial\");\n        showAlert(\"Your email address is already confirmed — refresh the page to continue.\", \"error\");\n        return;\n      }\n      setResendState(\"sent\");\n    } catch {\n      setResendState(\"initial\");\n      showAlert(\"Sorry, something went wrong. Please try again.\", \"error\");\n    }\n  };\n\n  return (","sourceCodeStart":13,"sourceCodeEnd":49,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/components/EmailConfirmationBanner.tsx#L13-L49","documentation":"EmailConfirmationBanner resends the confirmation email via POST resend_confirmation_email_settings_main_path. The catch (line 45) shows 'Sorry, something went wrong. Please try again.' whenever the request is not ok — the inner code throws a bare `new Error()` — or when any exception escapes, including a RateLimitError raised inside request() for a 429, whose server-written explanation and retry-after are therefore lost to the generic string. The success:false path (nothing left to confirm) is handled separately and gracefully.","triggerScenarios":"POST returning 401 (session expired) or 429 (resend endpoint rate-limited — the common case, since resends are throttled); or a network failure — all fall into the bare catch and produce the same generic message.","commonSituations":"Seller clicks Resend repeatedly and trips the per-email throttle; banner left open past session expiry; flaky connection on mobile.","solutions":["Check the Network tab: a 429 means the user just has to wait — the generic string misdescribes that as a malfunction.","Handle RateLimitError distinctly and show its message/retryAfter instead of the fixed string.","If 401, reload the page to refresh the session, then resend.","Add a cooldown on the Resend button client-side so users do not trip the server throttle at all."],"exampleFix":"// before\nif (!response.ok) throw new Error();\n} catch {\n  showAlert('Sorry, something went wrong. Please try again.', 'error');\n}\n\n// after — keep the server's throttle wording instead of masking it\n} catch (e) {\n  if (e instanceof RateLimitError) {\n    showAlert(e.message, 'error');\n    return;\n  }\n  assertResponseError(e);\n  showAlert('Sorry, something went wrong. Please try again.', 'error');\n}","handlingStrategy":"try-catch","validationCode":"const canResend = (state: string): boolean => state !== 'sending';\n\nif (!canResend(resendState)) return; // stop double-clicks from tripping the server throttle","typeGuard":"const isRateLimitError = (e: unknown): e is RateLimitError => e instanceof RateLimitError;","tryCatchPattern":"} catch (e) {\n  if (e instanceof RateLimitError) {\n    showAlert(e.message, 'error'); // keep the server's 'wait N minutes' wording\n    return;\n  }\n  assertResponseError(e);\n  showAlert('Sorry, something went wrong. Please try again.', 'error');\n}","preventionTips":["Handle RateLimitError before the generic catch — resends are throttled server-side and 429 is the expected failure.","Disable the Resend button while in 'sending' state to prevent throttle-tripping double clicks.","A 401 here usually means session expiry — suggest a page reload in the message.","Keep the success:false branch (nothing left to confirm) distinct, as the code already does — that path needs different wording than an error."],"tags":["http","email","resend","rate-limit","banner"],"backgroundTag":"http-request-failed","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}