Repository navigation
Conversation
The e2e's slow public RPC made /verify answer 500 internal_error, so a seller could not tell an outage it may retry from a bug of ours. An upstream call past its 10 s on every RPC URL, or a read of ours that failed on every URL, now answers 503 rpc_unavailable with Retry-After: 5. Upstream's unexpected_verify_error and every other thrown error stay 500. Co-Authored-By: Claude <209825114+claude[bot]@users.noreply.github.com>
… into lore-0046-verify-rpc-unavailable # Conflicts: # docs/architecture/m1-spine.md
The reviews of #152 found that FallbackRpc wraps its own timeouts in RpcUnavailableError, so RpcTimeoutError never reaches /verify. The docs now name the ledger read as the only read of ours that gives a 503, say a hung Horizon answers 503 too, and list 503 in the status convention. Co-Authored-By: Claude <209825114+claude[bot]@users.noreply.github.com>
The http-500 alarm counts 500 only, so after the 503 an RPC hang on /verify paged nobody. A verify_rpc_unavailable_total counter now feeds a verify-rpc-unavailable event alarm, listed in 0027's alarm and metric notes. With several RPC URLs the first failure decided the status, so the same outage gave 503 or 500 by which URL came first. Upstream's unexpected_verify_error now wins over a throw, and any other throw over a timeout: the 503 means every URL timed out. Co-Authored-By: Claude <209825114+claude[bot]@users.noreply.github.com>
… into lore-0046-verify-rpc-unavailable
Merged develop, whose alarm tests ask every production alarm for a section in docs/monitoring.md and list production's alarm names. Co-Authored-By: Claude <209825114+claude[bot]@users.noreply.github.com>
Upstream's verify reads Horizon on every payment with no timeout, so a hung Horizon times out every RPC URL and the alarm blames the RPC. Co-Authored-By: Claude <209825114+claude[bot]@users.noreply.github.com>
A counter of its own repeated what http_requests_total already counts. A [route, status] set for /verify and 503 now feeds the verify-rpc-unavailable alarm; the counter is gone. Co-Authored-By: Claude <209825114+claude[bot]@users.noreply.github.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When the RPC gave no answer,
/verifyanswered500 internal_error. A seller could not tell an outage it may retry from a bug of ours. The MCP e2e met it on the slow public testnet RPC.RPC_URLSentryinternal_error,http_errorlogrpc_unavailable,Retry-After: 5,verify_rpc_unavailablewarn logRpcUnavailableError)internal_errorrpc_unavailableinternal_errorrpc_unavailable(it blames the RPC)unexpected_verify_errorhttp-500counts 500 only)verify_rpc_unavailable_total+1,verify-rpc-unavailableevent alarmUnchanged: every verdict, valid or not, stays a 200. Upstream's
unexpected_verify_errorstays a 500, because it doesn't say whether the RPC failed. Other thrown errors stay a 500./settledoesn't change. A seller on@x402/corestill answers the buyer 402 for both 500 and 503: its client throws on any non-2xx answer withoutisValid.Evidence
Before: the MCP e2e's run 2 on 2026-11-04, from our facilitator's log, then the buyer saw
payment_rejected:{ "event": "rpc_exhausted", "component": "verify", "tried": [{ "reason": "timeout" }] } { "event": "http_error", "error": { "name": "UpstreamTimeoutError", "message": "upstream verify gave no answer within 10000 ms" } }New test, before the change:
verify-route.test.ts"answers 503 rpc_unavailable when the RPC gave no answer", one case per error class: 3 failed.Order of URLs, new test before the change:
upstream-failover.test.ts"decides the same whatever endpoint comes first" failed:promise rejected "UpstreamTimeoutError: a: upstream verify …" instead of resolving.After (
c7fdd716):verify-route.test.ts25 of 25, counting the counter per 503;upstream-failover.test.ts18 of 18; the whole facilitator suite 1006 passed, 3 skipped;metrics-catalog31 of 31;infra337 of 337, with the new alarm checked against 0027's alarm list. Typecheck,pnpm lintandformat:checkpass.Whole facilitator suite: 975 passed, 1 failed, 3 skipped. The failure is
settle.test.ts"never lets a waiting repeat end a claim reopened since it read the earlier one". That file doesn't touch/verify, and it passed 5 of 5 runs alone, so it looks like the timing flakiness task 0074 tracks.Not run: the MCP e2e or a real RPC outage. The test fakes the thrown error.
Considered and rejected
isValid: falseand an RPC reason — it says the payment is invalid, which nothing checked.unexpected_verify_errortoo — upstream also gives it for its own errors on a strange payload, so a retry could repeat forever.http-500— a lease or drain 503 on/settleis routine on every deploy./settle's checks — its own PR, fix(lore-0046): answer rpc_unavailable when /settle's checks get no answer #159:/settleanswers in x402 bodies (AD-6).Merge Danger
Door: two-way
Revert the commit. A seller that already reads 503 then sees 500 again.
Blast Radius: API
A client or alert that counts 5xx sees the same total. One that reads the status code or
errorsees a new value. Thehttp-500alarm no longer fires on these hangs; the newverify-rpc-unavailablealarm does once infra is deployed.Retry-After: 5is the owner's choice, not a measurement: one ledger.🤖 Generated with Claude Code