BRC-326 Wallet

Wallet Failure Presentation

This document specifies what a wallet MUST say when a request fails, and what an application MUST do with it. BRC-1 requires every wallet error to carry a human-readable description so that "the application can represent the error for the user", and never says what representing one involves. BRC-100 fixes the envelope as {status, code, description} . That is

Crumbs

How it connects

Relationships

Authoritative content mirror

Full upstream text

View source ↗
Rendered from wallet/0326.md at the indexed upstream commit

Abstract

This document specifies what a wallet MUST say when a request fails, and what an application MUST do with it.

BRC-1 requires every wallet error to carry a human-readable description so that "the application can represent the error for the user", and never says what representing one involves. BRC-100 fixes the envelope as {status, code, description}. That is the entire specification of failure in the corpus. The consequence is that every wallet vendor writes its own prose for the same conditions, and every application pipes a string it did not write, cannot localise and cannot shorten, straight to a user at the worst moment of their session.

This document closes the gap the way BRC-184 closed it one layer over, where a prompt reading [2, "auth message signature"] was identified as a thing a user cannot act on. It defines a closed core vocabulary of eight failure classes, the three machine-readable fields that carry them, and the rule that an application MUST NOT display a raw description without the structure around it. The vocabulary is closed and extended at reserved dispatch points in the manner of BRC-98, BRC-99 and BRC-111, so that it can grow without a registry and without a second document to widen it.

One class has no equivalent in any wallet today: uncertain delivery, meaning the request may have taken effect and nobody can establish whether it did. BRC-185 already distinguishes not-sent from unknown, because a network timeout cannot prove no transaction was created. Almost no wallet surfaces that distinction, so a user whose payment may or may not have happened is shown the same screen as a user whose payment definitely did not.

Motivation

Failure is the most frequent user-facing event in a wallet that is not a success, and it is the only one with no standard at all.

What the corpus currently requires

BRC-1 states it in one line:

All errors must have a human-readable description field that describes the error. This allows the application to represent the error for the user.

That sentence contains the whole problem, and the operative word is represent. Representing an error for somebody is an authoring act: choosing the words, the length, the register, and the moment. BRC-1 asks the application to perform it, and hands over a finished sentence in the wallet's voice, which is the one input that makes authoring impossible. The field gets relayed instead, because relaying is the only thing a bare string supports.

Nothing says what that string contains, who it addresses, what register it is written in, how long it may be, whether it may name internal machinery, or whether the application may show it unmodified. Every wallet answers differently, and the application, which owns the screen, has no basis on which to edit what it was given.

Why the vocabulary can be closed

BRC-184 exists because an identifier that is exact and meaningless produces a prompt a user cannot act on. Its solution was not to rename the identifier but to supply a description alongside it, with rules about failing open, about never presenting an absent description as danger, and about always retaining the exact value underneath.

Errors are the same shape of problem, arriving more often and at worse moments, and nothing equivalent exists. The difference is that BRC-184 needed a registry because protocol identifiers are open-ended and invented by third parties. Failure classes are not open-ended. A wallet can fail for a small number of structurally distinct reasons, and that set is enumerable, so the vocabulary can be closed and no registry or steward is needed to maintain it.

The four questions a user actually has

Tim Neusesser and Evan Sunwall's error-message guidelines for Nielsen Norman Group ask that a message describe the issue concisely and precisely, offer a remedy rather than merely stating the problem, sit close to the error's source, take a positive tone without blaming the user, and use human-readable language with error codes hidden or shown only for diagnosis. Translated into a wallet, a user meeting a failure has four questions and today's description answers at most one:

  1. What happened?
  2. Whose fault was it, and specifically, was it mine?
  3. What can I do now?
  4. Is it safe to try again?

The fourth is the one that matters most and is almost never answered, because answering it requires the wallet to know whether the request reached anything. BRC-185 section 6 already carries that knowledge in its delivery field, which is not-sent before dispatch and unknown where dispatch may have occurred and no authoritative result exists. That document says plainly that a network timeout cannot prove no transaction was created.

A user who is told "payment failed" when the truth is "we do not know" has been told something false about their money. They will retry, and they may pay twice.

What we propose the user sees

One class carries the argument for the whole document, so it is the one shown first. A wallet has dispatched a payment, the network went quiet, and no authoritative result came back. There is no vocabulary for that state today, so it is reported as the nearest tidy one.

Payment failed

Error: transaction broadcast timeout. Please try again.

[ Try again ]

Every line of that is a defect. The operation is not known to have failed. The sentence is the wallet's, in the wallet's language, at the wallet's length, on a screen the wallet has never seen. And the control is how a user pays twice.

Under this document the wallet returns class: uncertain-delivery, fault: none and retry: check-first, and the application writes this.

We do not know whether this payment went through

Your wallet sent it and the network did not answer. It may have arrived.

Check your balance before sending it again.

[ Check my balance ] [ Close ]

Three things changed. The heading names the state the wallet is in rather than a neater one next to it. The remedy is a check rather than a repeat, which is section 6.3's rule for check-first. And no control on the surface sends the payment again, because none may until the user knows.

What this costs if it stays unspecified

Two wallets fail the same way and produce two different sentences, so a user cannot learn what a recoverable failure looks like. An application cannot localise, because it is relaying a string in whatever language the wallet chose. A support request contains a sentence no one on the support side wrote. And the uncertain case is presented as a definite one, which converts an ambiguity into a double spend by the user's own hand.

Prior Art

Source What it supplies Where it lands
BRC-1, Ty Everett The description field and the instruction that the application represent the error for the user, which is the clause this document completes Section 6.1, and the note below on what representing requires
BRC-100, Ty Everett The {status, code, description} envelope that carries all of this, unchanged here Section 3.1
BRC-184, Ty Everett The precedent and the template: a description alongside an exact identifier, failing open, never presenting absence as danger The whole approach, and section 5.3
BRC-185 section 6, Ty Everett and Deggen The delivery distinction between not-sent and unknown, and the statement that a network timeout cannot prove no transaction was created Section 4.7, the uncertain-delivery class
BRC-98, BRC-99, BRC-111, Ty Everett The reserved dispatch-point pattern that lets a closed vocabulary be extended without a registry Section 7
BRC-219, Ty Everett That a refusal is an explicit error rather than a hang, and that a denial completes the original request Sections 4.3 and 8.1
BRC-151, BitcoinSvGuide That permission surfaces should describe consequences rather than internal terminology Section 5.1
Tim Neusesser and Evan Sunwall, Nielsen Norman Group Describe the issue precisely, offer a remedy, place it close to its source, do not blame the user, hide error codes Sections 5.1, 5.3 and 6.7
Jakob Nielsen, response-time limits That ten seconds is the limit of a user's attention on a task Section 4.7, the deadline past which a wallet owes the uncertain class
WCAG 3.3.1, Error Identification That an error be identified and described to the user in text Section 5.1, which says what that text contains, and sections 6.8 to 6.10, which say how it reaches the user

Where this sits

Several documents describe one arc, from a user who has no wallet to a user whose request has just failed. None of them states the arc, so it is stated here, and this document's own place in it is marked. Each is independently implementable and none depends on another having been read.

Moment Document What it owns
Arriving with no wallet BRC-110 Zero friction, deferred onboarding, and a benefit explained in context at the point of use
Being offered one BRC-137 Device detection, catalog-resolved install paths, fallback login, and the connect decision flow
Choosing among them BRC-300, pull request #224 The connect control and its vocabulary, chooser neutrality, and the sponsorship behind placement
The browser's permission BRC-325, pull request #330 What precedes the loopback dialog, and the honest reporting of a refusal
Reaching the wallet BRC-185 Substrate discovery and selection, coverage states, and connection states
When something fails This document What a failure says, and what an application may do with it

Documents named without a link are submitted and not yet merged. They are named rather than linked so that this document resolves correctly whatever order the set lands in.

The arc continues past this document in both directions and this document does not reach into either. Before a failure there is the confirmation the wallet shows and the user answers, which is what produces the refusal section 4.3 classifies. After one there is the question of what an application may claim about a payment that did go through, and separately the question of reviewing and withdrawing the grants a user has already given. Nothing here specifies any of those, and nothing here forecloses them: a later document that does will find the classes of section 4 already in place and will need no change to them.

What BRC-1 asks for, and what this supplies

BRC-1 gives one reason for the description field: "This allows the application to represent the error for the user." The verb is represent, not relay, and the distance between the two is this document.

Representing an error means writing it: in the application's language, at its length, in its register, for a screen it can see. That is the work BRC-1 assigns to the application. What the application receives is a finished sentence in the wallet's voice, which supports forwarding and discarding and nothing in between.

So this document does not contradict BRC-1. It supplies the inputs that clause has always required and never named: a class, a fault party, and a retry safety. Section 6.1's prohibition on displaying a raw description is that same instruction read strictly, and section 6.2 keeps the wallet's sentence available alongside, attributed, for the application that wants it.

Nothing in BRC-1 or BRC-100 changes. The envelope is the same, code is untouched, and a wallet that adds none of the three fields remains conforming to both.

Specification

The key words MUST, MUST NOT, SHOULD, SHOULD NOT, and MAY in this document are to be interpreted as described in RFC 2119.

1. Terminology

  • Failure: any BRC-100 response whose status is error.
  • Class: one of the eight structural categories in section 4, carried in the failure's machine-readable form.
  • Fault party: the party whose state or decision produced the failure. One of user, application, wallet, counterparty, network, or none. none means nobody erred, which is true of most failures, and counterparty is available only on the class in section 4.8.
  • Retry safety: whether repeating the identical request is safe, unsafe, or safe only after a check.
  • Presentation: what an application shows a user as a result of a failure.

2. Scope

2.1 This document governs failures of BRC-100 calls, and the presentation of those failures by the application that made them.

2.2 It does not govern transport failures below the wallet interface, which BRC-185 section 6 already types, except where such a failure surfaces as a BRC-100 error.

2.3 It adds no method, no endpoint and no wire format. It constrains the content of one existing field, adds three optional sibling fields, and places obligations on the application that receives them.

3. The envelope

3.1 What a wallet returns

A failure MUST carry the BRC-100 fields unchanged, and SHOULD carry three more:

Field Required Content
status Yes, existing error
code Yes, existing The wallet's machine-readable code, unchanged by this document
description Yes, existing Governed by section 5
class SHOULD One of section 4's eight values, or an extension under section 7
fault SHOULD One of user, application, wallet, counterparty, network, none
retry SHOULD One of safe, unsafe, check-first

3.2 A wallet that omits class, fault or retry is conforming but has left the application unable to meet section 6, and the application's fallback under section 6.4 is the result.

3.3 code remains the wallet's own. This document does not standardise it, because existing codes are deployed and a renaming would break consumers for no user-visible gain.

4. The eight classes

Each class below states what it means, the usual fault, and the usual retry. A wallet MUST NOT use a class whose meaning does not fit merely because no other fits better; section 4.8 exists for that.

Class Fault Retry A presentation says It MUST NOT say
insufficient-funds none check-first There is not enough to cover this, and the shortfall where the wallet gives it Anything implying the user erred
locked none safe The wallet is there and needs unlocking Missing. Disconnected. Not installed
permission-denied user unsafe The request was declined, and what the user can still do That declining was a mistake, or anything that asks again
policy-blocked none unsafe A standing rule refused this, and where that rule lives That the user declined. That trying again will help
unreachable network or none safe The wallet could not be reached just now Missing. Absent. Not installed
invalid-request application unsafe This site sent a request the wallet could not accept Anything asking the user to correct it
uncertain-delivery none check-first The outcome is unknown, and the way to find out Failed. Succeeded. Anything offering a bare retry
unavailable wallet or counterparty varies, and MUST be supplied The wallet cannot do this now, and whether waiting helps Any of the other seven

The table is normative and the subsections below carry the reasoning. Where the two appear to differ, the subsection governs.

4.1 insufficient-funds

The wallet holds less spendable value than the request needs. Fault none: the user has not erred, and neither has anybody else. Retry check-first, since retrying after funding succeeds and retrying before it does not.

A presentation MUST NOT imply the user did something wrong, and SHOULD state the shortfall where the wallet discloses it.

4.2 locked

The wallet is present and will not act until the user authenticates to it. Fault none. Retry safe.

BRC-185 section 5 states that lock state is not absence. A presentation MUST NOT describe a locked wallet as missing, disconnected or uninstalled.

4.3 permission-denied

The user was asked and refused, which is the denial BRC-219 requires the wallet to return as an error on the original request. Fault user, in the sense of authorship rather than blame. Retry unsafe within the same interaction.

A presentation MUST NOT re-ask automatically, MUST NOT describe the refusal as an error on the user's part, and MUST NOT retry. The user answered the question.

4.4 policy-blocked

A standing policy refused before any user was asked. BRC-181 spend policies, BRC-204 allowances, BRC-179 capability manifests and BRC-116 grants are the mechanisms. Fault none. Retry unsafe until the policy changes.

A presentation MUST distinguish this from permission-denied. Nobody was asked, so telling a user they declined something is false, and telling them to try again is useless.

4.5 unreachable

The wallet could not be contacted. Fault network or none. Retry safe.

A presentation MUST NOT describe an unreachable wallet as an absent one, which is BRC-185 section 4.1's rule that a blocked scan is not evidence of absence, and BRC-325 (pull request #330), whose whole subject is the browser side of this.

4.6 invalid-request

The request was malformed or asked for something the interface does not define. Fault application. Retry unsafe.

This is the one class whose presentation is primarily for a developer. A presentation MUST NOT ask the user to correct it, and SHOULD say plainly that the application made a bad request, because that is true and because the alternative is a user trying to fix something they cannot reach.

4.7 uncertain-delivery

The request may have taken effect and the wallet cannot establish whether it did. Fault none. Retry check-first, always.

This is BRC-185 section 6's delivery: 'unknown' raised to the user-facing surface. A presentation MUST NOT describe the operation as failed, MUST NOT describe it as succeeded, and MUST NOT offer a retry control that repeats the request without a check. It MUST tell the user that the outcome is unknown and name the way to find out: a balance, a list of actions, a transaction identifier if one exists.

A wallet MUST use this class wherever it cannot prove the request did not take effect. The temptation is to report a clean failure because it produces a tidier screen, and a tidier screen that causes a second payment is the worst outcome in this document.

A wallet MUST NOT hold a request open indefinitely in order to avoid reaching this class. Where a dispatch deadline passes with no authoritative result, the wallet MUST return uncertain-delivery rather than continue to wait, and SHOULD do so within ten seconds. That is Jakob Nielsen's limit for holding a user's attention on a task: past it the user has already concluded that something went wrong, and a wallet still waiting has left them to guess which something. Waiting longer does not make the honest class less true, and it is available at any point.

4.8 unavailable

The wallet understood the request, holds no objection to it, and cannot perform it now for a reason outside the other seven: a resource limit, a dependency that is down, a feature not implemented in this build. Fault wallet, or counterparty where the thing that failed is a party to the request rather than part of the wallet, such as a peer under BRC-185 that refused or could not complete its side. This is the only class that carries counterparty, because it is the only one whose failure can belong to somebody who is neither of the two parties on the screen.

Retry varies with the reason, so a wallet supplying class: unavailable MUST supply retry with it. Section 3.1 makes that field a SHOULD in general; here the class says nothing on its own, and without retry the application's fallback under section 6.4 returns the value it was just handed.

A presentation MUST NOT dress this as any of the other seven. A wallet that reports a missing feature as insufficient-funds has lied in a way the user will act on.

5. The description

5.1 What it must do

The description MUST answer, in the user's vocabulary, what happened and what can be done now. It MUST NOT:

  1. name internal machinery: method names, class names, field names, stack frames, HTTP status codes, or the wallet's own code;
  2. imply the user erred, except where fault is user and the error is an actual choice they made;
  3. assert an outcome the wallet has not established, which is section 4.7's prohibition stated once more because this is where it is violated;
  4. exceed two sentences.

BRC-151 states the principle for this corpus: describe consequences rather than internal terminology.

5.2 What it is not

The description is not the presentation. It is the wallet's best sentence for a surface it cannot see, written without knowing the application's language, layout, audience or how much room there is. Section 6 governs what the application does with it.

5.3 Absence

A wallet that cannot produce a meaningful description MUST supply the class and MUST NOT supply a filler string. "An error occurred" is worse than nothing, because it occupies the space a real sentence would have had and tells the application the field is populated. This follows BRC-184's rule that a neutral indication of absence is appropriate and a fabricated one is not, and the Nielsen Norman Group guidance names that same string as its example of a message with no context.

6. What the application does with it

6.1 An application MUST NOT display a raw description as the whole of its presentation. It is a wallet's sentence about a surface it has never seen.

6.2 An application that has class, fault and retry MUST compose its own presentation from them, in its own language and register, and MAY show the wallet's description alongside, attributed to the wallet.

6.3 A presentation MUST reflect retry:

  • safe: a retry control MAY be offered and MAY be prominent.
  • check-first: a retry control MUST NOT be offered without the check beside it, and the check MUST be the primary action.
  • unsafe: no retry control.

6.4 An application that receives no class MUST treat the failure as unavailable with retry: check-first, and MUST NOT infer a class by matching on code or on the text of description. String matching on another vendor's prose is how a localisation change becomes a double spend.

6.5 A presentation MUST NOT be a toast or any other auto-dismissing surface where retry is check-first or unsafe. A user who looked away has lost the only notice that their money may be in an uncertain state.

6.6 An application MUST NOT retry automatically where retry is unsafe or check-first, regardless of what its own backoff logic would otherwise do. BRC-185 section 6 already states this one layer down, where an adapter "MUST NOT retry a wallet method automatically after uncertain delivery". The rule is restated here because the layer that actually holds the retry logic is usually the application's, and a transport that obeys the rule underneath an application that does not has achieved nothing.

6.7 A presentation MUST appear where the user acted. A failed payment belongs at the payment, not in a notification tray, a console, or a screen the user has to navigate to, which is the error-message guidance that a message sit close to its source. Where the action had no single location, the presentation belongs where the user's attention already is.

6.8 A presentation MUST be identified to assistive technology when it appears. WCAG 3.3.1 requires that an error be identified and described in text, and a message that is merely drawn is not identified to somebody who is not looking at that region. A surface that appears without taking focus MUST carry a live-region role; one that takes focus MUST be labelled by its own heading.

6.9 A presentation MUST take focus where retry is check-first or unsafe. Those are the two cases in which the user has something to decide and the wrong default is to carry on, and a message that can be scrolled past is a message that will be.

6.10 A presentation MUST NOT convey the class by colour, icon or position alone. The class determines what the user may safely do next, and a user who cannot distinguish the red surface from the grey one is the user most likely to press the control that sends a payment twice.

7. Extension

7.1 The eight classes are closed. A wallet MUST NOT invent a ninth value for class.

7.2 A future specification MAY define additional classes at a reserved dispatch point, in the manner of BRC-98, BRC-99 and BRC-111: the value p followed by a space-free scheme identifier and the scheme-defined remainder.

7.3 An application that does not recognise an extension class MUST treat it as unavailable under section 6.4. An extension that is unsafe to treat that way is not a valid extension.

7.4 An extension MUST NOT redefine, narrow or override any of the eight. In particular it MUST NOT provide a tidier alternative to uncertain-delivery.

8. Relationships

8.1 BRC-219 requires that a wallet present a prompt or return an error, and never leave a permission-required request silently pending. The error it returns on denial is this document's permission-denied, and the liveness rules of that document are unchanged.

8.2 This document governs what happens when the answer to a confirmation is no, when the wallet never got as far as asking, or when the request could not be completed after a yes. A failure and the confirmation that preceded it are the same surface at different moments, and SHOULD be presented in the same place. This document places no requirement on the content of a confirmation, which is a separate subject.

8.3 BRC-185 section 6's ConnectionError is below this interface. A controller translating one to the other MUST map delivery: 'unknown' to uncertain-delivery and MUST NOT map it to anything else.

9. Conformance

Condition Required behaviour
Any failure {status, code, description} as today, SHOULD add class, fault, retry
Request may have taken effect class: uncertain-delivery, retry: check-first, never reported as failed or succeeded
User refused class: permission-denied, no automatic re-ask, no retry
Policy refused before asking class: policy-blocked, distinguished from a refusal
Wallet locked class: locked, never presented as absent
Wallet unreachable class: unreachable, never presented as absent
Malformed request class: invalid-request, fault application, user not asked to fix it
No class fits class: unavailable, never a misfitting one of the other seven
class: unavailable retry supplied with it, since the class alone says nothing
Dispatch deadline passed, no authoritative result uncertain-delivery returned rather than held open, within ten seconds
Description Two sentences at most, no internal machinery, no asserted outcome
No meaningful description available Class supplied, description omitted, no filler
Application has class Composes its own presentation; wallet description attributed if shown
Application has no class Treats as unavailable with check-first; MUST NOT match on code or text
retry: check-first Check is the primary action, retry not offered alone
retry: unsafe No retry control, no automatic retry
check-first or unsafe Not presented in an auto-dismissing surface, and takes focus
Any presentation Appears where the user acted, identified to assistive technology, class not carried by colour alone
Unrecognised extension class Treated as unavailable

10. Surfaces that fail conformance

  1. A wallet's description shown as the entire error message.
  2. An operation whose outcome is unknown presented as failed.
  3. A retry button on an uncertain-delivery.
  4. A toast carrying an uncertain delivery.
  5. A locked or unreachable wallet described as not installed.
  6. A policy refusal worded as though the user declined.
  7. A user asked to correct an invalid-request.
  8. A missing feature reported as insufficient-funds.
  9. An error occurred as a description.
  10. An application inferring a class by matching on another vendor's error text.
  11. An automatic retry of an unsafe failure, because the application's generic HTTP layer did not know this was different.
  12. A ninth class invented by a wallet rather than defined at the reserved dispatch point.
  13. A failure carried by a red border and nothing else.
  14. An uncertain delivery drawn on screen and announced nowhere, so a screen-reader user hears nothing.
  15. A wallet that waits rather than admit it does not know.

What Is Checkable, and What Is Taken on Trust

Fact Who can check it How
A failure carried a class The application The response
An uncertain delivery was not presented as a failure Anybody Induce one and read the screen
No retry control appeared on an unsafe failure Anybody Deny a permission and look
The description names no internal machinery Anybody Read it
The presentation was announced Anybody A screen reader
The class survives without colour Anybody Greyscale, or a screen reader
The application did not string-match on code A reviewer The source
Claim Why it cannot be checked What stands in for a check
The class is the right one Only the wallet knows why it failed Section 4.8, which gives an honest place to put the ones that do not fit
An uncertain delivery really is uncertain A wallet could report certainty it does not have, or uncertainty it does not have Nothing. A wallet that misreports its own certainty is constrained by no interface standard, including this one
The user understood what to do next No mechanism reaches a mind Section 5.1's content rules
The retry was actually safe Safety is a property of the wallet's state at that moment check-first exists because the honest answer is often "look before you do"

What It Buys, and What It Costs

Party What they get What it costs them
User A failure that says whose fault it was and whether it is safe to try again, and an uncertain outcome that is described as uncertain Nothing
Application Three machine-readable fields instead of a foreign-language sentence, so failures can be localised, shortened and routed Composing presentations for eight classes, where one alert(description) was sufficient before
Wallet vendor Its error prose stops being the user interface of every application that calls it Classifying its own failures honestly, including admitting uncertainty it currently hides behind a clean failure
Support A failure a user can describe over a phone Nothing

One thing deserves saying plainly. The honest classification is the expensive one. A wallet that reports uncertain-delivery is telling its user that it does not know what happened to their money, which reads as incompetence and is in fact the opposite. The clean failure is cheaper, tidier, and causes the double spend. An ecosystem that is not prepared to show the uncertain case should not adopt this document, because the other seven classes are tidying and this one is the point.

Security Considerations

An error message is an oracle. A description that explains precisely why an operation was refused can tell an attacker things about a wallet's state, its balances, its policies and its grants. Section 5.1's two-sentence bound limits the damage by accident; wallets MUST consider it deliberately, and SHOULD prefer a less specific description where the specific one discloses a balance or a policy to a caller that has not been granted it.

fault: user is a label, not a judgement, and will be read as one. A presentation built from it can easily imply the user erred when all they did was decline. Section 5.1 point 2 and section 4.3 exist because that implication is both unkind and inaccurate, and because a user who is told that refusing was a mistake refuses less carefully next time.

String matching on another vendor's description is a supply chain vulnerability. An application that branches on the text of an error inherits its control flow from whoever writes that wallet's copy, including whoever translates it. Section 6.4 forbids it and names the failure mode, because this is the shortcut every integrator reaches for when class is absent.

An attacker benefits from a tidy failure. Where a request may have taken effect, an adversary who can induce a clean "failed" message can induce a second attempt. Section 4.7 is a security requirement in that light, not only an honesty one.

Privacy Considerations

A failure discloses wallet state to the application that caused it. insufficient-funds reveals a bound on the balance. policy-blocked reveals that a policy exists and roughly what it covers. Wallets SHOULD disclose the class without the particulars where the caller has not been granted the underlying information, and MUST NOT include a balance, a policy body or a grant list in a description.

An application's error telemetry inherits all of this. An application that logs failures verbatim is logging wallet state. Applications SHOULD log the class and withhold the description.

Implementations

No new method, no endpoint, and no change to the existing envelope.

Wallet side. Three optional sibling fields on the existing error object, and a classification at the point each failure is raised. A wallet that already distinguishes its internal failure reasons has the information; what it lacks is the obligation to express it in a shared vocabulary.

Application side. A switch on eight values, and a presentation per class. The important line is the default case, which section 6.4 requires to be unavailable with check-first rather than a guess.

The translation from BRC-185. A controller sitting between the two maps ConnectionError to this envelope. DISCOVERY_BLOCKED becomes unreachable, USER_CANCELLED becomes permission-denied, CONFIGURATION_ERROR becomes invalid-request, and anything carrying delivery: 'unknown' becomes uncertain-delivery regardless of its code.

Verification. No cryptographic values and no wire format, so there is nothing to publish as vectors. Section 9's table is the conformance surface, and a wallet can be exercised against most of it by inducing each condition.

Failure Modes

What went wrong How it shows Detected by Attributable to
Uncertain delivery reported as failure A user retries and pays twice The user, on their balance The wallet
Retry offered on an uncertain delivery The same, faster The user The application
Application matched on error text A wallet update or a translation changes application behaviour Nobody, until it breaks The application
Locked wallet shown as absent A user is told to install software they already have The user, with irritation The application, or the wallet that gave no class
Policy refusal worded as a user refusal A user tries to change a decision they never made The user The application
Description carried internal machinery A user reads a method name and learns nothing Anybody The wallet
Description disclosed a balance to an ungranted caller Nothing visible Nobody The wallet
Automatic retry of an unsafe failure Duplicate effects Sometimes nobody The application's generic transport layer, which never knew
The wallet admitted it did not know Nothing went wrong The user, who can now check Nobody. This is the system working

References