Skip to content

docs(cashu): error-codes classifier + enum sync spec#1104

Closed
orveth wants to merge 1 commit into
masterfrom
docs/cashu-error-classifier-spec
Closed

docs(cashu): error-codes classifier + enum sync spec#1104
orveth wants to merge 1 commit into
masterfrom
docs/cashu-error-classifier-spec

Conversation

@orveth

@orveth orveth commented May 21, 2026

Copy link
Copy Markdown
Contributor

Summary

Spec doc for a minimal-change classifier that gives every cashu mint error code a single source of retry/recovery policy, plus a sync of app/lib/cashu/error-codes.ts to the current NUT spec.

Draft because this PR carries the spec only. Implementation will follow in separate PRs once the design is reviewed here.

Why

Today, mint-error handling is scattered: each mutation has its own `instanceof MintOperationError` retry-skip, service-level recovery branches inline a legacy-code + Nutshell-message-string check, and many mutations have `throwOnError: true` that crashes the React boundary on transient mint blips. There's also a cluster of open bugs (#1089 / PR #1090, #837, #421, #425, #673, #708) all touching the same gap.

The proposal: one shared `classify(err)` returning `'transient' | 'permanent' | 'already-resolved' | 'unhandled'`, plus a `normalizeCode(err)` helper that centralises legacy-value + Nutshell pre-0.16.5 message-string fallback in one place. Strictly request-level — services keep their flow-specific recovery logic.

Critical finding from the spec sync work

The current `error-codes.ts` enum has two active collisions with the current NUT spec (https://github.com/cashubtc/nuts/blob/main/error_codes.md):

Const Current value Spec value Spec's actual code at that slot
`TRANSACTION_NOT_BALANCED` `11002` `11005` `11002 Proofs are pending`
`UNIT_NOT_SUPPORTED` `11005` `11013` `11005 Transaction is not balanced`

If a spec-compliant mint returns `11002` meaning "proofs pending" (an already-resolved-shaped condition), agicash currently treats it as `TRANSACTION_NOT_BALANCED` (wallet bug). Same shape for `11005`.

Plus two more drifted codes (`OUTPUT_ALREADY_SIGNED 10002→11003`, `TOKEN_VERIFICATION_FAILED 10003→10001`) and 10 missing codes (notably `11002 PROOFS_ARE_PENDING` and `11004 OUTPUTS_ARE_PENDING` which deserve already-resolved handling).

Renumbering is safe — every callsite uses `CashuErrorCodes.NAME`, zero raw-number references (verified via grep).

Scope (in)

  1. Sync `app/lib/cashu/error-codes.ts` to current NUT spec.
  2. New `app/lib/cashu/error-classify.ts` with `classify(err)` + `normalizeCode(err)`.
  3. Replace `instanceof MintOperationError` retry-skip in 5 mutations with `classify(err) === 'transient'`.
  4. Replace `throwOnError: true` on cashu-touching mutations with `(err) => classify(err) === 'unhandled'`.
  5. Service-level recovery branches use `normalizeCode(err)` (one source of truth for legacy compat).

Scope (out — explicit follow-ups)

Relation to PR #1090 / issue #1089

PR #1090 wraps `MintOperationError → DomainError(error.message)` at the service boundary as an immediate fix for #1089. The classifier subsumes it — `DomainError` always classifies as `permanent`, and `MintOperationError` with any code (including custom `20739`) also defaults to `permanent`. Recommendation: revert the PR #1090 wrap commit and use the classifier instead.

Estimated implementation diff

  • `error-codes.ts` — ~40 LOC (4 value changes + 10 new entries)
  • `error-classify.ts` — ~80 LOC (new file)
  • Hook updates — ~50 LOC across 5–10 mutations
  • Service recovery refactor — ~30 LOC (inline → `normalizeCode`)
  • Tests — ~150 LOC

Total: ~350 LOC, single follow-up PR, no breaking changes.

Open implementation questions

  • Test coverage strategy — unit-test `classify`/`normalizeCode` directly + one E2E against a closed-loop gift-card mint to confirm the unknown-code path?
  • `20003 MINTING_DISABLED` and `20007 QUOTE_EXPIRED` ship as `permanent` (hours-scale, not session-scale transient) — OK?
  • `onError` per-mutation review during implementation: some currently `failSendQuote()`/`failSwap()` on `MintOperationError`; switching to `classify === 'permanent'` should be equivalent for existing codes but needs case-by-case confirmation.

Test plan

This PR adds only a docs file — no code or runtime changes.

  • Spec reviewed and approved by team
  • Open implementation questions resolved
  • Implementation PR(s) opened against this spec

🤖 Generated with Claude Code

Spec for a minimal-change classifier that gives every cashu mint error
code a single source of retry/recovery policy, plus a sync of the
`error-codes.ts` enum to the current NUT spec.

The enum sync is independently urgent: two enum values
(`TRANSACTION_NOT_BALANCED = 11002`, `UNIT_NOT_SUPPORTED = 11005`)
collide with current-spec codes (`11002 Proofs are pending`,
`11005 Transaction is not balanced`) and actively misclassify
spec-compliant mints.

Subsumes the immediate fix in #1089 / PR #1090 and lays the foundation
for #837 #421 #425 #673 #708. Implementation will follow in separate
PRs; this PR carries the spec only.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@vercel

vercel Bot commented May 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agicash Ready Ready Preview, Comment May 21, 2026 5:24pm

Request Review

@supabase

supabase Bot commented May 21, 2026

Copy link
Copy Markdown

This pull request has been ignored for the connected project hrebgkfhjpkbxpztqqke because there are no changes detected in supabase directory. You can change this behaviour in Project Integrations Settings ↗︎.


Preview Branches by Supabase.
Learn more about Supabase Branching ↗︎.

@gudnuf

gudnuf commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

superseeded by #1118

@gudnuf gudnuf closed this Jun 2, 2026
@gudnuf gudnuf deleted the docs/cashu-error-classifier-spec branch June 2, 2026 19:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants