0xMiden / 0xMiden/wallet

Guardian operator unreachable shows a raw TypeError and a new failed row per retry

Ouverte
#779 0 commentaires 0 réactions 0 personnes assignées Voir sur GitHub
feedback-form
Langage dominant
TypeScript
Étoiles
5
Forks
28
Merge moyen
9 h 50 min
PR mergées (30 j)
123

Description

## Summary
When the Guardian operator's server cannot be reached at the network level (connection refused / timed out, no HTTP response), every Guardian-signed transaction fails at stage `creating-proposal` and the only thing the user sees is the raw error **`TypeError: Failed to fetch`** — in the transaction detail, in Activity ("Transaction failed") and in Pending notes ("Claim failed — tap Retry"). Nothing says the Guardian operator is down, that funds are safe, or that it will be retried; no connectivity banner appears. Auto-consume then retries on its backoff (5 → 10 → 20 min … up to 24 h) and writes a **new terminal Failed row per attempt**.

Observed during a real outage of the LambdaClass operator (`miden-guardian.lambdaclass.com`, 178.104.116.212) on 2026-08-26 from ~04:04Z: ports 443/80 unreachable from three independent vantage points while the testnet node and the OpenZeppelin guardian answered normally. Five received notes (1–5 MIDEN) produced 16 Failed rows in under an hour, all with the same bare error. Funds not at risk (pre-submit), but users cannot tell an operator incident from a broken wallet, and once the operator is back, notes stay unclaimed until they manually tap Retry.

The wallet already classifies and requeues the adjacent transient guardian failures (409 pending-conflict, 429 rate-limit, prover outage). The no-HTTP-response case is the one still missing.

## Root cause

1. `src/lib/miden/transaction/index.ts:874` sets stage `creating-proposal`; the consume path then makes the guardian calls (multisig service load `src/lib/miden/guardian/index.ts:109`, create proposal `:213`) with no timeout, no abort, no retry (the conflict-retry wrapper only retries 409) and no fallback.
2. A transport failure throws a bare `TypeError` with no `status` / `code`, so in the guardian catch block (`index.ts:331`) it misses every arm — 409 requeue (:479), prover-outage requeue (:496, gated on stage `proving`), 429 requeue (:531-545), locked / aborted — and reaches the terminal fail call (:556).
3. The terminal path (`src/lib/miden/transaction/cancel.ts:32-63`) stores the raw error because the stage-based message mapping (`src/lib/miden/transaction/constants.ts:78-113`) has no entry for `creating-proposal`.
4. The network-error classifier that already recognizes "failed to fetch" (`src/lib/miden/activity/connectivity-classify.ts:20-37`) is never used by the transaction pipeline; the connectivity state (`src/lib/miden/activity/connectivity-state.ts:46`) has no guardian category; the only guardian-unavailability string in the locale file belongs to the switch-guardian screen.
5. Auto-consume re-queues on the lifetime-failure backoff (`src/lib/miden/transaction/initiate.ts:184-202`), producing a new Failed row each time; History renders "Transaction failed" + raw error (`History.tsx:180-186`), and the Pending tab's 2-minute claiming-flag stall (`src/lib/store/hooks/claiming-reconcile.ts:19`) causes the on/off "consuming" spinner.

## Fix

1. Classify a guardian call with no HTTP response (fetch TypeError, DNS/connect/TLS failure, timeout) as "guardian operator unreachable", reusing the existing network-error classifier.
2. In the pre-submit stages (`creating-proposal`, `signing-proposal`) route it through the existing requeue-with-cooldown path used for 409 / 429 / prover outage — single row per note, next-eligible time stamped.
3. Add a guardian category to the connectivity state and show the banner with the operator name; map the transaction error to the plain-language message above in the transaction detail, Activity and Pending notes; keep the raw error under a technical-details toggle.
4. Add a request timeout to guardian calls in the transaction path.
5. On the next successful guardian call (or guardian sync), kick the processing loop so waiting notes are claimed immediately and clear the banner.
6. Add a connection-refused / timeout mode to the e2e `guardian-fault` harness with a spec covering the above.

## Related

#​617 (429 → requeue, closed), #​313 (retry storm, closed), #​312 (409 backpressure stall — server reachable, open), #​252 / #​596 (node banner), #​771 (Failed rows persist in Activity — this feeds it). None cover the no-HTTP-response case or the missing operator-unavailable messaging.

## Environment

- **Platform:** Extension
- **Error code:** NODE_UNREACHABLE

## Attachments

![Screenshot 2026-08-26 131311.png](https://github.com/user-attachments/assets/4ea978ef-f752-4e1d-85b4-25c560b9158f)

---
*Filed automatically from the in-app feedback form by an anonymous reporter. Pipeline operated by @Ivanlomoljo26; reply here and the operator will see it.*

Guide de contribution

Ouvrir le guide de contribution

Évaluation

Cette issue n'a pas encore été évaluée.

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.