{"record":{"id":"d812a32a17136c1d","repo":"antiwork/gumroad","slug":"responsedata-error-message-d812a3","errorCode":null,"errorMessage":"responseData.error_message","messagePattern":"responseData\\.error_message","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"error","filePath":"app/javascript/data/publish_product.ts","lineNumber":14,"sourceCode":"import typia from \"typia\";\n\nimport { request, ResponseError } from \"$app/utils/request\";\n\ntype TogglePublishStateResponse = { success: true } | { success: false; error_message: string };\n\nexport async function setProductPublished(id: string, publish: boolean) {\n  const response = await request({\n    url: publish ? Routes.publish_link_path(id) : Routes.unpublish_link_path(id),\n    method: \"POST\",\n    accept: \"json\",\n  });\n  const responseData = typia.assert<TogglePublishStateResponse>(await response.json());\n  if (!responseData.success) throw new ResponseError(responseData.error_message);\n}\n","sourceCodeStart":1,"sourceCodeEnd":16,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/data/publish_product.ts#L1-L16","documentation":"ResponseError thrown at publish_product.ts:14 whose message is responseData.error_message from POSTing Routes.publish_link_path(id) or Routes.unpublish_link_path(id): setProductPublished toggles a product's publish state and the server answered { success: false, error_message }, which is forwarded verbatim. There is no response.ok check — the endpoint is trusted to speak the TogglePublishStateResponse envelope (typia.assert enforces it at runtime).","triggerScenarios":"Publish refused because required attributes are missing (no price on a paid product, no content files attached, required description or cover missing); unpublish refused for products referenced by active bundles or with in-flight purchases; permission failures on products the caller doesn't own.","commonSituations":"Seller clicks 'Publish' on a half-configured draft; unpublishing a product embedded in a live bundle; UI optimistically flipping the toggle before the server confirms and getting refused; empty error_message strings after backend refactors leaving users with blank error toasts.","solutions":["Read the thrown error_message — it names the unmet publish precondition; satisfy it (upload required assets, set price or quantity) and retry the toggle","Refetch the product after failure so the UI reflects the server's authoritative published state, not the optimistic one","Guard double-clicks: disable the publish/unpublish control while the POST is pending","If the toggle silently reverts, watch the publish/unpublish POST in DevTools — a success:false with a quiet toast is the usual culprit","If error_message is empty in production, fix the endpoint to always populate it so this error is diagnosable"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"import { assertResponseError } from '$app/utils/request';\nassertResponseError(e); // e.message names the unmet publish precondition","tryCatchPattern":"try {\n  await setProductPublished(id, publish);\n} catch (e) {\n  assertResponseError(e);\n  showError(e.message); // e.g. missing thumbnail or price — show the server's reason\n  await refetchProduct(id); // revert the optimistic toggle to the server's state\n}","preventionTips":["Show error_message verbatim; it tells the seller exactly what to fix before publishing","Refetch the product after failure so the publish toggle reflects server truth","Disable the toggle while the POST is in flight","Fix the endpoint if error_message ever comes back empty — the client depends on it"],"tags":["publish","product","server-validation","dashboard"],"backgroundTag":"json-api-error-envelope","analyzedSha":"afeacbd394069a1cbf0c6c50ee8e900925050370","analyzedAt":"2026-08-21T17:58:52.159Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}