{"record":{"id":"9b75134077813545","repo":"antiwork/gumroad","slug":"json-error-message","errorCode":null,"errorMessage":"json.error_message","messagePattern":"json\\.error_message","errorType":"exception","errorClass":"ResponseError","httpStatus":null,"severity":"error","filePath":"app/javascript/data/product_dashboard.ts","lineNumber":51,"sourceCode":"  });\n\n  const json = typia.assert<{ success: true; archived_products_count: number } | { success: false; errors: string[] }>(\n    await response.json(),\n  );\n  if (!json.success) throw new ResponseError(json.errors[0] || \"Failed to unarchive product\");\n  return json.archived_products_count;\n}\n\nexport async function duplicateProduct(permalink: string, productName: string) {\n  const response = await request({\n    url: Routes.product_duplicates_path(),\n    method: \"POST\",\n    accept: \"json\",\n    data: { id: permalink },\n  });\n\n  const json = typia.assert<{ success: true } | { success: false; error_message: string }>(await response.json());\n  if (!json.success) throw new ResponseError(json.error_message);\n\n  await pollForProductDuplication(permalink, productName);\n}\n\nasync function pollForProductDuplication(permalink: string, productName: string) {\n  const response = await request({\n    url: Routes.product_duplicate_path(permalink),\n    method: \"GET\",\n    accept: \"json\",\n  });\n\n  const json = typia.assert<{ status: string; error_message?: string }>(await response.json());\n  if (json.status === \"product_duplication_failed\") {\n    const reason = json.error_message\n      ? `Sorry, failed to duplicate '${productName}': ${json.error_message}`\n      : `Sorry, failed to duplicate '${productName}'. Please try again.`;\n    throw new ResponseError(reason);\n  } else if (json.status === \"product_duplicated\") {","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/antiwork/gumroad/blob/afeacbd394069a1cbf0c6c50ee8e900925050370/app/javascript/data/product_dashboard.ts#L33-L69","documentation":"ResponseError thrown at product_dashboard.ts:51 whose message is json.error_message from the POST to Routes.product_duplicates_path(): the duplication kickoff endpoint answered { success: false, error_message } and the client forwards the server's reason. This is the synchronous validation result — the asynchronous part (the actual copy job) is polled separately and fails via error 89.","triggerScenarios":"The duplicate endpoint refuses to even enqueue duplication: account/product limits reached, product type or feature not supported for duplication, permalink missing/foreign, or the user lacking permission. The message text is whatever the controller put in error_message.","commonSituations":"Hitting a per-account product-count cap on a large catalog; duplicating a product type whose files live in external storage still being migrated; double-click on 'Duplicate' enqueueing twice (second call may fail with 'duplication already in progress'); stale dashboard session where the session check fails and the endpoint answers success:false with a generic message.","solutions":["Read the thrown error_message — it is the server's specific refusal reason","If it reports a duplicate already in progress, wait for the existing job (the poller in error 89 will report it) instead of enqueueing again","Address the stated constraint (product limit, unsupported type) and retry once resolved","Disable the duplicate action while a request is in flight to avoid double-enqueue refusals"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":"import { assertResponseError } from '$app/utils/request';\nassertResponseError(e);","tryCatchPattern":"try {\n  await duplicateProduct(permalink, productName);\n} catch (e) {\n  assertResponseError(e);\n  showError(e.message); // server's refusal (limits, unsupported type, in-progress)\n}","preventionTips":["Show error_message verbatim — it distinguishes limits from transient refusals","Only offer 'Retry' when the message indicates a transient cause","Prevent double-enqueue: hide or disable Duplicate while a duplication is pending"],"tags":["product","duplicate","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"}