antiwork/gumroad · error · ResponseError
typia.assert<{ error: string }>(json).error
Error message
typia.assert<{ error: string }>(json).error What it means
resendToNonOpeners POSTs to the non_opener_resend path; on non-ok it runs typia.assert<{ error: string }>(json) and throws ResponseError with the server's error text. Two distinct failures live on this line: the expected ResponseError when the server sent { error }, and a typia TypeError thrown from inside the assert when the failure body doesn't match that exact shape — an HTML error page, an empty body, or a body keyed message instead of error.
Source
Thrown at app/javascript/data/installments.ts:176
});
if (!response.ok) throw new ResponseError();
// `count` is null when the audience is too large to count within the request; the
// resend itself still works (recipients are resolved in a background job).
return typia.assert<{ count: number | null; recently_resent: boolean; audience_filtered_out: boolean }>(
await response.json(),
);
}
export async function resendToNonOpeners(externalId: string) {
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
- Inspect the failing response body in the Network tab — does it actually contain { error: string }?
- Make the extraction defensive: parse once, type-guard the error key, and fall back to a generic message instead of typia.assert on an error path.
- Align the server: on failure render json: { error: ... } with a matching status.
- If the failure is 429, note request() already raises RateLimitError before your handler — don't re-parse that body.
Example fix
// before
if (!response.ok) throw new ResponseError(typia.assert<{ error: string }>(json).error);
// after
if (!response.ok) {
const message =
typeof json === 'object' && json !== null && 'error' in json && typeof (json as { error: unknown }).error === 'string'
? (json as { error: string }).error
: 'Could not resend to non-openers.';
throw new ResponseError(message);
} Defensive patterns
Strategy: try-catch
Type guard
const hasErrorBody = (json: unknown): json is { error: string } =>
typeof json === 'object' && json !== null && 'error' in json && typeof (json as { error: unknown }).error === 'string'; Try / catch
try {
const result = await resendToNonOpeners(externalId);
} catch (e) {
if (!(e instanceof ResponseError)) {
// typia TypeError: the failure body wasn't { error: string } — log it whole
console.error('Unexpected error body', e);
showError('Could not resend to non-openers.');
} else {
showError(e.message);
}
} Prevention
- Never run typia.assert on an error-path body; use a type guard with a fallback message.
- Pin every internal endpoint to one error-body key (error vs message) and enforce it in tests.
- Return RateLimitError semantics for 429 rather than parsing bodies at the call site.
- Add a controller test asserting the failure body shape for every json endpoint.
When it happens
Trigger: A 4xx whose body is JSON { error } → ResponseError with that message. A proxy or Rails exception page (HTML), an empty 4xx body, or a body like { message: '...' } → typia TypeError (invalid type at $input.error) instead of the intended ResponseError.
Common situations: A rate-limited internal endpoint whose 429 body has no error key; a controller rescue block returning head :no_content on failure; an environment behind an HTML error proxy where the JSON contract breaks only on the error path.
Related errors
- typia.assert<{ message: string }>(await response.json()).mes
- ${data.error}
- ${responseData.error}
- Something went wrong.
- responseData.error_message
AI-assisted analysis of antiwork/gumroad@afeacbd394 (2026-08-21).
Data as JSON: /api/errors/3f40c3b99981369b.
Report an issue: GitHub.