Skip to content

fix(lore-0046): answer rpc_unavailable when /settle's checks get no answer - #159

Merged
karolko9 merged 1 commit into
developfrom
lore-0046-settle-rpc-unavailable
Nov 9, 2026
Merged

karolko9 merged 1 commit into
developfrom
lore-0046-settle-rpc-unavailable

Conversation

@karolko9

@karolko9 karolko9 commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

/settle runs the same checks as /verify before it claims a payment. When the RPC gave those checks no answer, /settle answered 500, while /verify (#152) answers 503 rpc_unavailable for the same outage. /settle now answers in an x402 body with the reason it already has for an RPC outage.

 settle(payload, requirements)
   checked = check(payload, requirements)
     OutOfTimeError                      -> settle_exact_stellar_timeout, nothing claimed
+    UpstreamTimeoutError                -> settle_exact_stellar_rpc_unavailable, nothing claimed
+    RpcUnavailableError                 -> settle_exact_stellar_rpc_unavailable, nothing claimed
     anything else                       -> thrown: 500 internal_error
Situation Before After
Upstream's call in the checks gets no answer within 10 s on every RPC URL 500 internal_error 200, success: false, settle_exact_stellar_rpc_unavailable, no record
The checks' ledger read fails on every URL 500 internal_error the same 200

Unchanged: the reason already answers RPC failures after the claim, and it is one of the "nothing sent" reasons, so a seller may repeat the same payment. Upstream's unexpected_verify_error in the checks stays a 500. /verify doesn't change here.

Evidence

  • New test, before the change: settle.test.ts "answers rpc_unavailable without claiming when the checks' RPC gave no answer", one case per error class: 2 failed, the error thrown out of settle().
  • After: both pass, and they check that nothing is submitted or recorded. settle.test.ts and settle-route.test.ts 177 of 177. The whole facilitator suite: 1006 passed, 3 skipped. Typecheck, pnpm lint and format:check pass.

Not run: the MCP e2e or a real RPC outage. The test throws the error from a stubbed check.

Considered and rejected

  • A 503 like /verify's — /settle answers every outcome in an x402 body (AD-6), and the reason already exists.
  • Mapping unexpected_verify_error too — upstream also gives it for a strange payload, so "repeat the same payment" could repeat forever.

Merge Danger

Door: two-way

Revert the commit.

Blast Radius: API

A seller that read a 500 from /settle in an outage now reads a 200 with success: false and a reason. Sellers on @x402/core treat both as a failed settlement. The http-500 alarm stops firing for this case; /settle failures with this reason show in the settled logs.

🤖 Generated with Claude Code

…nswer

/settle runs the same checks as /verify before it claims. An RPC that gave
them no answer was a thrown error, so /settle answered 500 while /verify
now answers 503 for the same outage. It now answers its own
settle_exact_stellar_rpc_unavailable in an x402 body, with nothing claimed,
so the same payment may be sent again.

Co-Authored-By: Claude <209825114+claude[bot]@users.noreply.github.com>
@karolko9
karolko9 merged commit c6edd45 into develop Nov 9, 2026
4 checks passed
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.

1 participant