Repository navigation
Conversation
…tead of zero - getAccountBalances throws AccountBalanceUnavailableException (an expected state, not sent to snap_trackError) when the account has no snapshot and is not confirmed unfunded, and schedules a deduplicated SynchronizeAccounts (at most once per 30 s per account), so Retry triggers a real sync. - Sync persists a confirmed-unfunded marker (AccountNotActivatedException) and emits a native 0 once. - An empty-body Horizon 404 is no longer read as 'account not found': it is a transport failure (e.g. a provider router rejecting a mis-routed, HTTP/2-coalesced request), not a confirmed unfunded account.
This was referenced Oct 26, 2026
fix(stellar): run account reads under the fetch policy and report stale balances as unavailable
#525
Closed
…ke-zero # Conflicts: # packages/stellar-wallet-snap/src/handlers/keyring/keyring.test.ts # packages/stellar-wallet-snap/src/handlers/keyring/keyring.ts # packages/stellar-wallet-snap/src/services/on-chain-account/OnChainAccountService.ts
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
@metamask/stellar-wallet-snapExplanation
Bug
A Stellar account the snap never managed to sync shows 0 XLM. The snap also reads an empty-body Horizon 404 (what the coalesced connection returns) as "account not found", and so confirms an unfunded, zero-balance account.
How to reproduce
release/13.54.0(acf000d) withyarn build:dev. For the "after" case, also build this branch and copy itsdist/bundle.js,snap.manifest.jsonandpreinstalled-snap.jsonintonode_modules/@metamask/stellar-wallet-snap. To see the wallet-side "Balance unavailable" + Retry, add core PR 10 and extension PR 12.stellar-mainnet.infura.iocan reuse the HTTP/2 connection already open to another*.infura.iohost (in our runs the Solana snap usually opened it first). The reply is an empty 404 without CORS headers, so the snap seesFailed to fetch. We reproduced this on a test machine where every*.infura.ioname resolved to the same IP. It depends on the network path and may not reproduce on every network. In our runs we moved the shared connection between hosts on purpose: we dropped the test browser's sockets while briefly blocking the other Infura hosts, which "heals" one chain and breaks the others.--disable-http2give no coalescing, and all four balances are correct.Expected: while the account was never synced, the balance is unavailable (not 0), Retry starts a real sync, and the not-synced state is not sent to Sentry.
Actual (before this change): XLM shows 0 (unfixed A: 0 in all 17 snapshots over 15 min, and after a reload).
Without a browser:
keyring.test.ts›getAccountBalances›throws AccountBalanceUnavailableException when the account was never synchronized, andutils.test.ts›isAccountNotFoundError›returns false for an empty-body 404 (""), which does not come from Horizon.Summary
getAccountBalancesno longer returns a confident native0for an account the snap has never synced. It now throwsAccountBalanceUnavailableException, an expected state that is never sent to Sentry, and schedules a deduplicated sync, so the wallet's Retry triggers a real sync. An empty-body Horizon 404 is no longer treated as "account not found".Why
In Chrome, HTTP/2 connection coalescing across
*.infura.iocan send the stellar snap's Horizon request over a connection opened for another Infura host. The provider router answers with an empty 404 and no CORS headers, which shows up asFailed to fetchor as an empty 404. Two code paths then turned that into a fake zero:getDefaultBalanceEntryreturned native 0 when the account was "not activated or not yet synced", so a user whose first sync failed saw 0 XLM.isAccountNotFoundErrortreated any HorizonNotFoundErroras "unfunded", including the router's empty 404.What changed
handlers/keyring/keyring.ts,exceptions.ts: when there is no snapshot and the account is not confirmed unfunded, throwAccountBalanceUnavailableException.isExpectedState.SynchronizeAccountsbackground event for the account, at most once per 30 s per account. A Retry therefore causes a real sync instead of re-reading the empty state.services/on-chain-account/*,services/sync/SynchronizeService.ts: sync persists a confirmed-unfunded marker when Horizon confirms the account does not exist (AccountNotActivatedException), and emits a native 0 once. Only a confirmed-unfunded account reports 0.services/network/utils.ts: newisEmptyHttpBody.isAccountNotFoundErrorreturns false for aNotFoundErrorwith an empty body.utils/errors.ts:shouldTrackErrorskips expected states and user rejections. The expected-state check survives the keyring SDK's re-serialisation, which turns the error into a plainError.index.test.tscovers the end-to-endsnap_trackErrorpath.Validation
Checked locally: the test files changed in this PR were run with the parent's source (tests from this branch, every other file from the parent), then on this branch.
Unit tests that fail before and pass after (parent:
80b21f6)KeyringHandler › getAccountBalances ›throws AccountBalanceUnavailableException when the account was never synchronized,returns zero native balance when the account is confirmed unfunded on-chain, and the 4 tests underon-demand sync for never-synchronized accounts(Retry schedules one deduplicated sync)isAccountNotFoundError ›returns false for an empty-body 404 ("")and(undefined), which does not come from Horizon; the 7isEmptyHttpBodycasesOnChainAccountRepository(8),OnChainAccountService(3) andSynchronizeService(3): the confirmed-unfunded marker, so that a real unfunded account still shows 0errors › shouldTrackError ›4 tests andentrypoints › ... › is re-created by handleKeyringRequest as a plain Error, yet not trackedFull run on this branch
jest --runInBandinpackages/stellar-wallet-snap: 93 suites, 1476 tests pass.tsc --noEmit(package): clean.Manual A/B
A = Chrome with default HTTP/2 (reproduces the coalescing); B = Chrome with
--disable-http2(control, no coalescing). Every run used the same test wallet and a localyarn build:devof extension 13.54.0 (acf000d), with the snaps and assets-controller wired into node_modules by hand. "Unfixed 13.54.0" is that build with no fixes. The fixes-vN builds are cumulative: each one has every fix up to that pass, so an "after" number shows this PR together with the others in that build. Run names (A-v6, B-v7, ...) are our local test passes on those builds; times are CEST.horizon/accounts10:33:54 → XLM 18.4010064 by 10:34:09snap_trackError(stellar)Risks
Changelog (
packages/stellar-wallet-snap/CHANGELOG.md, Unreleased → Fixed)References
Dependencies
snap.manifest.jsonshasum withyarn workspace @metamask/stellar-wallet-snap build. It is not included here.Stale non-EVM balances series (Chrome HTTP/2 coalescing across
*.infura.io):Checklist