Repository navigation
bug: send shows connecting to network until lightning connections is opened #1389
Description
Refs:
- [Bug]: On-chain send blocked by Lightning sync overlay after scanning address #1278
- 2.5.0 mainnet QA: node start doesn't recover from transient network failures; WALLET backup blocked by legacy Pubky session #1252
- Unified no-amount QR shows only Savings, never Spending #849
What happened?
Seen once, on 25 Sep 2026, on an Android emulator (API 36.1) running Bitkit dev 2.5.0 (190) on regtest, during a demo walkthrough: paying a contact showed "Connecting to network" for about 25 s, then an amount screen that offered only Savings. After Continue, the confirm screen stayed on "Connecting to network" for over 3 minutes. After opening Settings › Advanced › Lightning Connections once and starting Pay again, the amount screen offered Spending with the switch.
I read that as a channel that was usable all along and a stale cached state. That is an inference from the second try; I did not check the node's channel state during the stuck send.
The rest of this section is a reading of the code on master (4d13a1d), not a reproduction:
ui/sheets/SendSheet.kt:102-107covers the Send flow with the sync overlay while the node has channels and none is usable inlightningState:The condition is athasAnyChannels && lightningState.channels.none { it.isUsable }:105-106; the overlay is drawn at:576-586.lightningState.channelsis a snapshot that onlyLightningRepo.syncState()refreshes (LightningRepo.kt:1668-1678): at node start (:418), afterconnectToTrustedPeers()(:1226), after a successfulsync()(:756-758), onChannelPendingorChannelReady(:803-806), and after payments and channel opens or closes.- A channel that becomes usable again when its peer reconnects produces no LDK event.
AppViewModel.handleLdkEvent()(AppViewModel.kt:1347-1391, thewhenat:1364-1385) handles every node event, and none marks a reconnect;ChannelReadyfires once per channel. The background sync'sSyncCompletedrunsWalletRepo.debounceSyncByEvent()(WalletRepo.kt:302-310), which refreshes balances, transfers and activities but not the channel list. - The wait does not re-read either:
LightningRepo.waitForUsableChannels()(:1382-1421) re-reads channels only when the list is empty (:1396-1400). Otherwise it waits for a state change that nothing emits, up toCHANNELS_USABLE_TIMEOUT(:2186, 15 s), and the callers then checkcanSend()against the same snapshot. - Lightning Connections fixes it because opening the screen runs
lightningRepo.sync()(LightningConnectionsScreen.kt:90-92,LightningConnectionsViewModel.kt:151-152), whose success path callssyncState().
Why the snapshot stayed stale for 3+ minutes in that run is not explained: node start runs sync() right after connectToTrustedPeers() (LightningRepo.kt:434-441), and a successful sync calls syncState(). The stuck send's log was not saved.
Expected behavior
While the node has channels and none reads usable, Bitkit re-reads the channel list, so a reconnected channel is usable within about a second without a visit to Lightning Connections.
Steps to Reproduce
These are the steps of that one run on 25 Sep, not a recipe that triggers it on demand:
- Use a wallet with one Lightning channel to the LSP.
- Open a contact and tap Pay.
- Wait for "Connecting to network" to clear; the amount screen offers only Savings. Continue: the confirm screen stays on "Connecting to network".
- Open Settings › Advanced › Lightning Connections, go back and tap Pay again: Spending is offered.
I did not find out how the stale state arose in that run. Backgrounding the app so its node stops and restarts is my guess at a trigger and was not run as such. On 30 Sep, on an Android emulator (API 36.1, regtest, one channel to the LSP), two natural attempts did not trigger it: disconnecting the LSP peer in the app (the channel read not usable and the peer did not come back on its own for 100 s), and a cold start with the network off, then on (the app's own refresh ran 40 ms after the channel re-established, so the state was fresh).
Logs / Screenshots / Recordings
The log of the stuck send was not saved. The lines below are from a different run the same day, a wallet setup on an Android emulator (Bitkit dev 2.5.0 (190), regtest): a node start read the channel as not usable 23 ms before LDK logged it reconnected. Fields are elided, timestamps are as logged.
00:51:48.246 LightningRepo: Collected channel support summary for 'trusted peers connected': channelId='e6f5…f356', … ready='true', usable='false'
00:51:48.269 LDK: Reconnected channel e6f5…f356 with no loss
Bitkit Version
Bitkit dev 2.5.0 (190), regtest, 25 Sep 2026. The code cited above was read on master 4d13a1d, not on that build.
Device / OS
Android emulator, API 36.1
Reproducibility
Rarely (once or twice)
Seen once on 25 Sep; not reproduced in the attempts on 30 Sep.
Additional context
- Lightning related; one channel to the LSP; regtest.
- [Bug]: On-chain send blocked by Lightning sync overlay after scanning address #1278 covers the same overlay blocking on-chain sends while channels are really unusable. Here the channel works and only the cached state is stale.
- 2.5.0 mainnet QA: node start doesn't recover from transient network failures; WALLET backup blocked by legacy Pubky session #1252 (finding 2) is adjacent: it asks to wait instead of failing while the node rebuilds after a foreground. This issue starts once the node is running again and its channel snapshot is stale.
- Unified no-amount QR shows only Savings, never Spending #849 (closed, could not reproduce) showed the unified QR offering only Savings through the same
canSendcheck; it is the closest earlier sighting. - The open feat: add allowances for automatic paykit payments #1340 adds
PaykitAllowanceRepo.canPayNow(), which readslightningState.channels.any { it.isUsable }on the same snapshot, so allowance auto-pay also defers while the state is stale. - Other flows already re-read the node before they decide:
WalletViewModel.refreshBalances()(:496-499, transfer to savings),WalletViewModel.refreshReceiveState()(:569-574, Receive),AppViewModel.kt:2145(an invoice with an amount, beforecanSend) and the foregroundWalletRepo.syncNodeAndWallet()(:263; itslightningRepo.sync()call is at:277). Send and contact Pay use none of them, so Send is the flow that stays on the stale snapshot. - iOS keeps the same cached design (
WalletViewModel.swift:1124-1128copieslightningService.channels, which onlyLightningService.refreshCache()fills). Its Send sheet shows the overlay for Lightning payments only and gives up after 20 s (fix(send): resolve indefinite sync overlay wait bitkit-ios#590). I did not reproduce the stale state on iOS.
Proposal:
- In
waitForUsableChannels(), while channels exist and none is usable, re-read the channel list every second until one is usable orCHANNELS_USABLE_TIMEOUTends, as the empty-list branch already does once. Re-read channels and peers only;syncState()also callslistBalances(see [Bug]: Node.listBalances ANR in receive flow #1036). - While
SendSheetshows the overlay for unusable channels, run the same re-read, so the amount screen offers Spending once the channel is back. - Test: in
LightningRepoTest, start with a state that holds one channel that is not usable, let the service report it usable, and assert thatwaitForUsableChannels()returns well before the timeout.
Activity