antiwork/gumroad · error · ResponseError

json.error_message

Error message

json.error_message

What it means

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.

Source

Thrown at app/javascript/data/product_dashboard.ts:51

  });

  const json = typia.assert<{ success: true; archived_products_count: number } | { success: false; errors: string[] }>(
    await response.json(),
  );
  if (!json.success) throw new ResponseError(json.errors[0] || "Failed to unarchive product");
  return json.archived_products_count;
}

export async function duplicateProduct(permalink: string, productName: string) {
  const response = await request({
    url: Routes.product_duplicates_path(),
    method: "POST",
    accept: "json",
    data: { id: permalink },
  });

  const json = typia.assert<{ success: true } | { success: false; error_message: string }>(await response.json());
  if (!json.success) throw new ResponseError(json.error_message);

  await pollForProductDuplication(permalink, productName);
}

async function pollForProductDuplication(permalink: string, productName: string) {
  const response = await request({
    url: Routes.product_duplicate_path(permalink),
    method: "GET",
    accept: "json",
  });

  const json = typia.assert<{ status: string; error_message?: string }>(await response.json());
  if (json.status === "product_duplication_failed") {
    const reason = json.error_message
      ? `Sorry, failed to duplicate '${productName}': ${json.error_message}`
      : `Sorry, failed to duplicate '${productName}'. Please try again.`;
    throw new ResponseError(reason);
  } else if (json.status === "product_duplicated") {

View on GitHub (pinned to afeacbd394)

Solutions

  1. Read the thrown error_message — it is the server's specific refusal reason
  2. 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
  3. Address the stated constraint (product limit, unsupported type) and retry once resolved
  4. Disable the duplicate action while a request is in flight to avoid double-enqueue refusals
Defensive patterns

Strategy: try-catch

Type guard

import { assertResponseError } from '$app/utils/request';
assertResponseError(e);

Try / catch

try {
  await duplicateProduct(permalink, productName);
} catch (e) {
  assertResponseError(e);
  showError(e.message); // server's refusal (limits, unsupported type, in-progress)
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21). Data as JSON: /api/errors/9b75134077813545. Report an issue: GitHub.