Errors and failed payments
The model for every failure in a BSV product: plain words first, whether money moved, what to do next, and the code underneath for support.
When something fails, the person needs to know whether they lost money. We recommend one model for every failure, in four parts and in this order: what happened, in plain words; whether any money moved; what the user can do next; and, underneath, the code support will ask for.
Whether money moved is the part an error message must never leave out. Most failed payments fail before anything leaves the wallet. When that is true, say so.
Sending, in the daily spending wallet, covers the errors on a send screen in detail. This page is the general model, for apps, wallets and agents alike.
The four parts
What happened. One sentence, about the user's situation rather than the system's: "Anna's handle couldn't be looked up", not "Lookup failed".
Whether money moved. "Nothing was sent", "Nothing left your wallet for this", or, rarely, "This payment went through, but the receipt didn't arrive". If you do not know yet, say that, and say when you will.
What to do next. One action, as a button where there is one: add money, try again, choose another way to pay, ask for help.
The code. Kept for support, out of the way.
Do
Say whether money moved in every message that follows a payment attempt, including when the answer is that nothing left the wallet.
Do
End every error with one thing the user can do, as a button where there is one.
Do
Put the error code and technical detail in an expandable details area with a copy button, so support can ask for it.
Do
Keep what the user entered. Return them to where they were with the amount, recipient and note still filled in.
Don't
Write "Something went wrong" on its own. If you truly do not know what happened, say what you do know and whether money moved.
Who is speaking
A wallet and an app both see a failure, and only one of them should explain it. When the user refused something in their wallet, the wallet already knows, and the app only receives a rejection. When the network rejects a payment, the wallet has the detail and the app has the context.
Do
Explain a failure in the app when it is about the app's task, such as an article not unlocking, and in the wallet when it is about the user's money or permissions.
Retrying
Some failures are worth retrying, and some are dangerous to retry. The wallet library reports a broadcast it could not complete as a service error, and keeps it queued to try again. A second payment on top of a queued one can pay twice.
Don't
Offer "Try again" on a payment until you know the first attempt did not go out.
Do
Tell the user once when their wallet is retrying for them: "Your wallet will keep trying to send this. You don't need to pay again."
The failures
| What happened | Signal underneath | What to say | What to do next |
|---|---|---|---|
| The user cancelled in their wallet | The wallet call is rejected as denied | "You cancelled the payment. Nothing was sent." | Back to the review screen, everything filled in |
| Not enough money | WERR_INSUFFICIENT_FUNDS; the shortfall is moreSatoshisNeeded | "You need 1,200 more sats." | Add money, or lower the amount |
| A conflicting payment was seen first | WERR_REVIEW_ACTIONS, a result with status doubleSpend | "This payment didn't go through. Nothing left your wallet for it." | Check recent activity |
| Rejected by the network | A result with status invalidTx | "The network didn't accept this payment. Nothing was sent." | Try again once, since nothing went out; then contact support with the code |
| The network could not be reached | A result with status serviceError, or the app's own request fails | "Your wallet couldn't reach the network. It will keep trying." | Wait; do not pay again |
| The handle does not resolve | The lookup fails | Which part failed: no such handle, the handle's domain is unreachable, or its certificate did not check out | Check the spelling, or ask the recipient |
| The recipient cannot take a peer-to-peer payment | Only a plain address is offered | "Anna's handle only accepts a plain address. The payment can still go, without her wallet confirming it." | Continue, or choose another way |
| A permission was refused | Permission denied (BRC-116) | "Pixel Arcade can't save your progress without this." | Ask again when they next use the feature |
| A permission was revoked | The wallet asks the user again on the next call | "The Satoshi Vision needs your permission again." | Explain the prompt that is about to appear |
| An allowance is used up | The month's limit is reached | "You've used this month's 5,000 sats for The Satoshi Vision. It resets on 1 December." | Approve this one, raise the limit, or wait |
| An agent reached its budget | The wallet holding the agent's funds refuses | "Example Assistant has spent its 5,000 sats budget and has stopped." | Tell the owner; they top up or end the task |
| No wallet connected | The app has not connected, or the user is not signed in | "Connect your wallet to read this article." | Connect wallet |
| No wallet installed | The wallet library finds no wallet to talk to | "To pay here you need a BSV wallet." | Help them install one, and keep the rest of the app usable |
| Opened in an ordinary phone browser | A phone browser cannot reach a wallet at all | "To pay here, open this page in your BSV wallet's browser." | Copy link, and help them install a wallet |
The amounts and names in this table are examples. Show the real shortfall, the real limit and the real date.
Sending has the detail for a send screen: cancelling, not enough money, conflicts, rejection, handles and plain-address recipients. Legacy compatibility explains the recipient that only takes a plain address, and Paying by handle explains handles.
Money sent to an address on another network is a different kind of failure, because it does not fail at all. See Wrong-network payments.
Failures that are not errors
Two states look like failures and are not.
Waiting for the user. A request waiting on a wallet prompt can take minutes. The prompt liveness standard (BRC-219) says the app waits rather than timing it out. Show "Waiting for your wallet…", not an error.
A refusal. The permissions standard (BRC-116) says apps should treat a denied permission as an expected answer. It needs a sentence about what the app cannot do, not a red banner.
Don't
Show a refusal or an unanswered wallet prompt in an error style. Both are the user's choice, and red tells them they did something wrong.
When many payments fail
For an app charging many small amounts, one failure in a hundred is a rate, not an event. Charging per use covers deciding what happens then and telling the user once. The same applies to an agent, and the owner is the person to tell. See Letting an agent spend.
Open questions
- When a payment's outcome is unknown for a while, how long can a product say "We're checking" before people pay again?
- Should an app or the wallet explain a used-up allowance? Both see it, and the standard does not say which.
- Do people read "Nothing was sent" as reassurance, or does any failure message make them check their balance anyway?
Related
- Sending: errors on a send screen, in detail
- Connecting a wallet: no wallet, or none connected
- Paying by handle: resolving a handle before paying
- Wrong-network payments: the failure that does not look like one
- Permissions and allowances: refused, revoked and used-up grants
- Charging per use: failure as a rate
- Letting an agent spend: telling the owner
- Activity: where a failed payment is recorded
- Transactions: why a conflicting payment loses
Written from: BRC-100, BRC-116 §7.5 and §7.6, BRC-219, @bsv/sdk 4.0 wallet errors (WERR_INSUFFICIENT_FUNDS, WERR_REVIEW_ACTIONS, ReviewActionResultStatus), Sending, Letting an agent spend.
Checklist
The rules on this page, in a form you can work through.
9 rules
