# Caveats & Troubleshooting (/troubleshooting)



## Connect hangs, then fails after 90 seconds [#connect-hangs-then-fails-after-90-seconds]

butr enforces a 90-second connect timeout. If `connector.connect()` neither
resolves nor rejects in that window, butr rejects with
`Error("Connection timeout")`, which normalises to `{ kind: "Timeout" }`. This
catches wallets that hang without cleanup. Separately, `onSlowConnect` fires
once if a connect exceeds `slowConnectThresholdMs` (default `5000`) — use it
for a "still trying — check your wallet" hint.

## State isn't persisting and nothing errors [#state-isnt-persisting-and-nothing-errors]

Persistence is **fire-and-forget by design** — a failed write (quota exceeded,
IndexedDB shutdown, cross-tab conflict, cookie size limit) does not throw.
You'll only see it if you set `onStorageError`. The default with no callback is
`console.warn`. See [persistence](/concepts/persistence).

## An error shows up as `Unknown` [#an-error-shows-up-as-unknown]

[Error normalisation](/concepts/errors) is best-effort. A connector that
throws a shape `mapConnectionError` doesn't recognise lands in
`{ kind: "Unknown", cause }`. Inspect `cause` for the raw value. butr never
retries — you decide based on `kind`.

## "Request more accounts" does nothing on Solana [#request-more-accounts-does-nothing-on-solana]

SVM `requestAccounts` is effectively a no-op: Wallet Standard wallets expose
all accounts at once and have no picker. The call resolves cleanly (it refreshes
the list). Gate the button on `wallet.connector.capabilities.requestAccounts`
so it doesn't render for SVM.

## Ledger doesn't connect in Firefox/Safari [#ledger-doesnt-connect-in-firefoxsafari]

`@usebutr/ledger` uses WebUSB, which only exists in Chromium browsers (Chrome,
Edge, Brave, Arc). It's also signing-only — `sendTx`, `getBalance`, and
`getTransactionReceipt` reject. See the
[Ledger connector](/connectors/ledger).

## A previously-connected wallet doesn't come back on reload [#a-previously-connected-wallet-doesnt-come-back-on-reload]

Hydration is asynchronous because adapters announce asynchronously. The wallet
likely sat in `pendingIds` and its adapter hadn't announced yet.
`WalletManagerProvider` calls `tryRestoreFromPending` internally each time a
new adapter is announced when you pass `discovery`. You only need to call it
yourself if you implement a fully custom `WalletSource` outside the provider.
Inspect `onHydrated`'s `HydrationOutcome` to see which bucket each wallet fell into.

## It auto-reconnects when I don't want it to (or won't) [#it-auto-reconnects-when-i-dont-want-it-to-or-wont]

`isUserDisconnected` is **session-scoped**. It's set `true` after an explicit
disconnect or reset and cleared on the next successful connect or a fresh
session. It exists to suppress auto-reconnect immediately after a manual
disconnect. Read it with `useIsUserDisconnected()`.

## Two apps on the same origin clobber each other's wallet state [#two-apps-on-the-same-origin-clobber-each-others-wallet-state]

Storage keys are prefixed (`{prefix}:pool`, etc.) with default prefix `butr`.
Set a distinct `storageKeyPrefix` per app sharing an origin.

## Signature verification fails on Solana [#signature-verification-fails-on-solana]

Verify against the `signedMessage` returned by `signMessage`, not your input
bytes — Solana Wallet Standard wallets may prefix/re-encode the message. See
[signing](/guides/signing).

## UI flashes "not connected" on every reload [#ui-flashes-not-connected-on-every-reload]

You're rendering before hydration finishes. Gate the first render on
`useIsHydrated()`.
