Phase D follow-up: retry-policy switch for idempotent reads (#52) #64
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "phase-d-retry-switch"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The retry half of the 2026-05-26 error-handling agreement (#52, Phase D) — building on the typed
LnbitsRpcErroralready ondev(#61).What it does
withRetry(fn)retries an operation per the disposition of the error it throws:LnbitsRpcError.retryPolicy—operator_signer_unavailable/rate_limited→ backoff,internal_error(and absent/unknown codes) → retry-once;unauthorized,insufficient_balance, …) and unknown errors → rethrow immediately.The safety call — why reads only
Applied only to idempotent reads (
getWallet/getBalance/listWallets/getPayment/decodePayment+ thelnurlwread methods).create_invoice/pay_invoice/lnurlw_create_linkare deliberately NOT wrapped — a blind retry would mint a duplicate invoice/link or double-pay. Their errors surface for flow-level handling (the state machine / operator). This is the key reason the switch lives at the per-call read layer rather than as a blanket client retry or a generic XState loop.Safe to land now
An absent
error_codealready maps tointernal_error(retry-once), so even before lnbits emits codes, idempotent reads just get one transparent retry on a transient blip — no behaviour change otherwise.Tests
10 new (
retry.test.ts): success-first, transient-then-succeed, max-attempts exhaustion, retry-once semantics, terminal-no-retry, timeout-retry, unknown-no-retry, backoff progression,onRetrycallback. typecheck 12/12; lnbits 29, full suite green.Remaining Phase D follow-ups (not in this PR — need the live cash-out path)
invoice_already_paid→ success-equivalent at the cash-out watch/dispense path (the error layer already flags itterminal-idempotent/isIdempotentSuccess; wiring needs a path that can surface it).Do NOT use the MCP merge endpoint — merge via the Forgejo UI after review.
🤖 Generated with Claude Code