{"record":{"id":"7fffc7125f5d851c","repo":"antiwork/gumroad","slug":"something-went-wrong-7fffc7","errorCode":null,"errorMessage":"Something went wrong.","messagePattern":"Something went wrong\\.","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"error","filePath":"app/javascript/components/Settings/PaymentsPage/LegalGuardianSection.tsx","lineNumber":185,"sourceCode":"      city: formState.city,\n      state: formState.state,\n      zip_code: formState.zip_code,\n      accept_terms: formState.accept_terms,\n    };\n    // Only sent when the seller typed one. Sending the empty string would overwrite the identifier on\n    // file with nothing and quietly make a complete guardian incomplete again.\n    if (formState.individual_tax_id.trim() !== \"\") payload.individual_tax_id = formState.individual_tax_id.trim();\n\n    try {\n      const response = await request({\n        method: existing ? \"PUT\" : \"POST\",\n        accept: \"json\",\n        url: existing ? Routes.settings_guardian_path(existing.id) : Routes.settings_guardians_path(),\n        data: { guardian: payload },\n      });\n      if (!response.ok) {\n        const body = typia.assert<{ error?: string }>(await response.json().catch(() => ({})));\n        throw new ResponseError(body.error ?? \"Something went wrong.\");\n      }\n      // typia rather than a cast, so a response shape that drifts from this contract fails loudly\n      // here instead of writing undefined into the form and reporting success.\n      const { guardian } = typia.assert<{ guardian: Guardian }>(await response.json());\n\n      setFormState(guardianToFormState(guardian));\n      // The incomplete branch names the terms specifically: it is the only required field a seller\n      // can leave unset and still save, so it is the only reason this message ever appears.\n      showAlert(\n        guardian.has_completed_info\n          ? \"Your legal guardian's details are saved. Payouts will resume on your next payout date.\"\n          : \"Your legal guardian's details are saved, but payouts stay on hold until your guardian accepts the terms.\",\n        \"success\",\n      );\n      // Last, and after the local form state is already correct: this refetches the page's own\n      // guardian props, which is what moves the payout-hold notice. Doing it before the setState\n      // above would let the reload's re-render race the form's own update.\n      onSaved();","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/components/Settings/PaymentsPage/LegalGuardianSection.tsx#L167-L203","documentation":"Fallback in the legal-guardian (minor creator) payouts form. Saving sends a guardian payload via PUT/POST to settings_guardian(s)_path; on !response.ok it reads { error } from the JSON body ({} if unparseable) and throws ResponseError('Something went wrong.') when the server provided none. Successful saves re-read the guardian from the response and show payout-resume vs terms-pending messaging, so this error strictly means the save itself failed. The tax-id field is only sent when non-empty so an edit cannot wipe a stored value.","triggerScenarios":"PUT/POST /settings/guardians(/:id) returns non-2xx without body.error — controller validation of the guardian's personal details or individual_tax_id failing silently, 401/419 session or CSRF expiry, or a 500 HTML page.","commonSituations":"KYC/tax-id validation rejections not translated into body.error, long-lived settings tabs with expired sessions, or the guardian record deleted in another tab making the PUT target stale.","solutions":["Inspect the failed request's response body; this fallback means no error key was present.","Reload and retry to rule out session/CSRF expiry.","Verify required guardian fields (name, DOB, address) are complete and the tax id is well-formed when provided.","Server-side: pluck validation and Stripe errors into body.error so the fallback never masks the cause."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"const isGuardianSaveFailure = (response: Response, body: { error?: string }): boolean =>\n  !response.ok && !body.error;","tryCatchPattern":"try {\n  const response = await request({ method: existing ? \"PUT\" : \"POST\", accept: \"json\", url, data: { guardian: payload } });\n  if (!response.ok) {\n    const body = typia.assert<{ error?: string }>(await response.json().catch(() => ({})));\n    throw new ResponseError(body.error ?? \"Something went wrong.\");\n  }\n  const { guardian } = typia.assert<{ guardian: Guardian }>(await response.json());\n  setFormState(guardianToFormState(guardian));\n} catch (error) {\n  setFormError(error instanceof Error ? error.message : \"Couldn't save guardian.\");\n}","preventionTips":["Include per-field validation errors in body.error so guardians can self-correct.","Only send individual_tax_id when non-empty (as the code does) so edits don't wipe stored values.","Re-auth on 401 rather than showing the generic fallback."],"tags":["payments","legal-guardian","kyc","http","form-submit"],"backgroundTag":"http-request-failed","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}