antiwork/gumroad · error · ResponseError

Something went wrong.

Error message

Something went wrong.

What it means

ResponseError ('Something went wrong.') thrown at price_distribution.ts:68 when the POST to Routes.price_check_product_path(uniquePermalink) — the pay-what-you-want price-distribution check sent with { refresh, overrides } and an optional abort signal — returns non-ok (a 4xx; 5xx/429/network are handled inside request()). The endpoint computes the price histogram for a product so the buyer can sanity-check their offer; a 4xx means the permalink or the override values were rejected.

Source

Thrown at app/javascript/data/price_distribution.ts:68

  name: string;
  description: string;
  taxonomy_id: string | null;
  native_type: string;
  currency_code: string;
};

export const fetchPriceDistribution = async (
  uniquePermalink: string,
  { refresh = false, overrides, signal }: { refresh?: boolean; overrides: PriceCheckOverrides; signal?: AbortSignal },
): Promise<PriceDistribution> => {
  const response = await request({
    method: "POST",
    accept: "json",
    url: Routes.price_check_product_path(uniquePermalink),
    data: { refresh, overrides },
    abortSignal: signal,
  });
  if (!response.ok) throw new ResponseError();
  return typia.assert<PriceDistribution>(await response.json());
};

View on GitHub (pinned to afeacbd394)

Solutions

  1. Read the status in DevTools for the price_check POST: 404 points at the permalink, 422 at the overrides
  2. Validate overrides before calling: positive finite amount, min<=max, and required fields present (see validationCode below)
  3. Verify uniquePermalink comes from the currently loaded product, not from a cached or shared URL
  4. Keep passing the AbortSignal from the UI component so superseded checks cancel before they can fail
  5. If refresh=true flows hit RateLimitError, respect retryAfter instead of hammering refresh

Example fix

// before
if (!response.ok) throw new ResponseError();

// after
if (!response.ok) {
  const body = await response.json().catch(() => null) as { error?: string } | null;
  throw new ResponseError(body?.error ?? `Price check failed (${response.status})`);
}
Defensive patterns

Strategy: validation

Validate before calling

const validOverrides = (o: PriceCheckOverrides): boolean =>
  Number.isFinite(o.amount) && o.amount > 0 && (o.min == null || o.max == null || o.min <= o.max);

Type guard

import { RateLimitError, assertResponseError } from '$app/utils/request';
// distinguishes 'wait' (429) from a real failure
if (e instanceof RateLimitError) { /* wait e.retryAfter seconds */ } else { assertResponseError(e); }

Try / catch

try {
  return await fetchPriceDistribution(permalink, { overrides, refresh, signal });
} catch (e) {
  if (e instanceof RateLimitError) return scheduleRetry(e.retryAfter);
  assertResponseError(e);
  return null; // price hint is optional UI — degrade gracefully
}

Prevention

When it happens

Trigger: 404 when uniquePermalink does not match a live product (typo, deleted, unpublished, or a permalink from another environment); 422 when the overrides object contains values the server rejects (non-positive amount, minimum greater than maximum, unsupported currency, or an offer below a set floor); 401/403 when the check requires a session that has expired.

Common situations: Price check fired on a product page kept open after the seller unpublished it; overrides built from a form whose fields were emptied (amount=NaN → serialized as null → 422); multiple rapid edits triggering overlapping checks — stale ones are aborted via signal, but a permanently bad permalink keeps 404ing; heavy refresh=true polling from a stuck UI eventually hitting the 429 path (which surfaces as RateLimitError, not this error).

Related errors


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