antiwork/gumroad · error · ResponseError

typia.assert<{ message: string }>(await response.json()).mes

Error message

typia.assert<{ message: string }>(await response.json()).message

What it means

previewInstallment POSTs internal_installment_preview_email_path and on non-ok asserts the body is { message: string }, throwing ResponseError with that message. Same dual failure as its sibling at line 176: a well-formed { message } body yields the intended ResponseError, but any other failure body (HTML page, empty body, or a body keyed error) makes typia.assert throw a TypeError from inside the error path.

Source

Thrown at app/javascript/data/installments.ts:187

  const response = await request({
    method: "POST",
    accept: "json",
    url: Routes.internal_installment_non_opener_resend_path(externalId),
  });

  const json: unknown = await response.json();
  if (!response.ok) throw new ResponseError(typia.assert<{ error: string }>(json).error);
  return typia.assert<{ success: boolean }>(json);
}

export async function previewInstallment(externalId: string) {
  const response = await request({
    method: "POST",
    accept: "json",
    url: Routes.internal_installment_preview_email_path(externalId),
  });

  if (!response.ok) throw new ResponseError(typia.assert<{ message: string }>(await response.json()).message);
}

View on GitHub (pinned to afeacbd394)

Solutions

  1. Inspect the failing response body in the Network tab and check the key is message, not error.
  2. Standardize the installment endpoints on one error-body shape, or extract defensively with a type guard instead of typia.assert.
  3. Fix the server render path so JSON-only endpoints never fall through to an HTML error page.
  4. Show e.message (ResponseError) and log TypeError separately — they mean different defects.

Example fix

// before
if (!response.ok) throw new ResponseError(typia.assert<{ message: string }>(await response.json()).message);

// after
if (!response.ok) {
  const json: unknown = await response.json();
  const message =
    typeof json === 'object' && json !== null && 'message' in json && typeof (json as { message: unknown }).message === 'string'
      ? (json as { message: string }).message
      : 'Could not preview this installment.';
  throw new ResponseError(message);
}
Defensive patterns

Strategy: try-catch

Type guard

const hasMessageBody = (json: unknown): json is { message: string } =>
  typeof json === 'object' && json !== null && 'message' in json && typeof (json as { message: unknown }).message === 'string';

Try / catch

try {
  await previewInstallment(externalId);
} catch (e) {
  if (!(e instanceof ResponseError)) {
    console.error('Unexpected error body', e); // typia TypeError — body wasn't { message: string }
    showError('Could not preview this installment.');
  } else {
    showError(e.message);
  }
}

Prevention

When it happens

Trigger: A 4xx with a JSON { message } body → ResponseError carrying the server text (e.g., 'Installment has no email body'). A 4xx whose body is keyed error — which the sibling resend endpoint uses — or is HTML/empty → typia TypeError on $input.message.

Common situations: Two endpoints in the same file expecting different error-body keys; an exception-rendering path that returns HTML while handlers assume JSON; previewing an installment whose email template fails to render server-side.

Related errors


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