antiwork/gumroad · error · ResponseError

Failed to archive product

Error message

Failed to archive product

What it means

ResponseError('Failed to archive product') thrown at product_dashboard.ts:25 when the POST to Routes.products_archived_index_path(id: permalink) answers { success: false } with an empty or missing `error` field — the literal string is the fallback after `json.error ||`. So the server did refuse the archive, but its payload omitted the reason, and the user sees the generic fallback.

Source

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

    method: "DELETE",
    url: Routes.link_path(permalink),
    accept: "json",
  });

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

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

  const json = typia.assert<{ success: true } | { success: false; error: string }>(await response.json());
  if (!json.success) throw new ResponseError(json.error || "Failed to archive product");
}

export async function unarchiveProduct(permalink: string) {
  const response = await request({
    url: Routes.products_archived_path(permalink),
    method: "DELETE",
    accept: "json",
  });

  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({

View on GitHub (pinned to afeacbd394)

Solutions

  1. Reproduce with DevTools open and inspect the products/archived POST response body to see the real (empty-field) payload and status
  2. Check server logs for the archive guard that returned success:false — the reason lives there, not in the response
  3. Fix the endpoint to always populate `error` when returning success:false (see exampleFix) so clients can surface the true cause
  4. If the payload shape was renamed, align the typia assert on the actual envelope to catch drift via TypeError instead of silent fallback

Example fix

# app/controllers/products/archived_controller.rb (server side)
# before
render json: { success: false } # guard failed, no reason
# after
render json: { success: false, error: 'Product cannot be archived while linked to an active bundle' }
Defensive patterns

Strategy: try-catch

Type guard

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

Try / catch

try {
  await archiveProduct(permalink);
} catch (e) {
  assertResponseError(e);
  // generic fallback text means the server sent an empty `error` — check logs, then resync
  if (e.message === 'Failed to archive product') logEmptyArchiveError(permalink);
  showError(e.message);
}

Prevention

When it happens

Trigger: The archive endpoint returns success:false with error: '' (or the key absent): server-side guard rejected archiving (product state, permissions, dependent records) while the controller failed to populate the error field.

Common situations: Backend refactor changes the refusal payload from {error: '...'} to {errors: [...]} or {message: '...'} leaving `error` undefined; archiving a product already archived; archiving products owned by another account from a shared dashboard; the empty-field case making support tickets hard to diagnose because every user report says only 'Failed to archive product'.

Related errors


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