fix: answer the page when a background handler throws (closes #280)
All checks were successful
check / check (push) Successful in 46s
All checks were successful
check / check (push) Successful in 46s
handleRpc(...).then(sendResponse) had no .catch(), and sendResponse is the only
thing that settles the dApp's window.ethereum.request() promise. Any throw
inside handleRpc therefore sent nothing back: the content script posted nothing,
and the page's promise stayed pending forever with no error and no timeout,
indistinguishable from a slow wallet. handleRpc does real work -- state loads,
provider calls, transaction population, approval plumbing -- so "it does not
throw today" was not a property anyone was maintaining.
A rejected handleRpc now answers { code: -32603, message }. -32603 is the
JSON-RPC internal error EIP-1474 defines and EIP-1193 defers to for RPC-layer
failures; no EIP-1193 4xxx code describes "the wallet broke" and none was
invented for it. The cause is not put in the message: the page gets a stable
sentence, the background console gets the method and the throw, so the failure
is visible rather than swallowed.
The two async IIFEs behind AUTISTMASK_TX_RESPONSE and AUTISTMASK_SIGN_RESPONSE
are the same shape one level down. Every statement is inside a try, but a throw
from one of the catch blocks escapes as an unhandled rejection and neither the
popup nor the page is answered. Each gets a last-resort .catch() that settles
the approval through settleApproval() -- the existing chokepoint, with no new
delete or resolve -- and answers the popup. The transaction one reports the
broadcast stage, because it cannot tell whether the transaction reached the
network and that is the wording that does not invite a second send. Every other
message handler on the path is synchronous and cannot leave a promise pending.
Each of the three is driven by a real failure rather than a hook in the handler:
a rejecting extension-storage read, which getState() awaits unguarded, and a
failure classifier that throws while classifying a genuine verification failure.
All three were demonstrated failing against the unfixed code, the RPC one with
sendResponse at zero calls, which is precisely the page-side hang.
This commit is contained in:
19
TODO.md
19
TODO.md
@@ -45,6 +45,25 @@ undefined identifiers, which is how
|
||||
|
||||
# Completed Steps
|
||||
|
||||
- 2026-08-14: A background message handler that throws now rejects the page
|
||||
instead of hanging it. `handleRpc(...).then(sendResponse)` had no `.catch()`,
|
||||
and `sendResponse` is the only thing that settles the dApp's
|
||||
`window.ethereum.request()` promise — so any throw inside `handleRpc` left
|
||||
that promise pending forever, with no error and no timeout, indistinguishable
|
||||
from a slow wallet. It now answers `{ code: -32603, message }` (the JSON-RPC
|
||||
internal error EIP-1474 defines and EIP-1193 defers to; no EIP-1193 4xxx code
|
||||
describes "the wallet broke" and none was invented) and logs the method and
|
||||
the throw to the background console rather than swallowing them. The two async
|
||||
IIFEs behind `AUTISTMASK_TX_RESPONSE` and `AUTISTMASK_SIGN_RESPONSE` were the
|
||||
same shape one level down — every statement inside a `try`, but a throw out of
|
||||
a `catch` block escaping unhandled — and each got a last-resort `.catch()`
|
||||
settling the approval through `settleApproval()` and answering the popup; the
|
||||
transaction one reports the broadcast stage, because it cannot tell whether
|
||||
the transaction reached the network. Every other handler on the path is
|
||||
synchronous. All three are driven by real failures — a rejecting storage read,
|
||||
and a failure classifier that throws while classifying a genuine verification
|
||||
failure — and were demonstrated failing first, the RPC one with `sendResponse`
|
||||
at zero calls ([#280](https://git.eeqj.de/sneak/AutistMask/issues/280)).
|
||||
- 2026-08-12: EIP-1193 error codes now reach the page. `src/content/inpage.js`
|
||||
rebuilt every failure as `new Error(error.message)`, so the code the
|
||||
background produced and the content script relayed intact was dropped in the
|
||||
|
||||
Reference in New Issue
Block a user