You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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-107 covers the Send flow with the sync overlay while the node has channels and none is usable in lightningState:
The condition is at :105-106; the overlay is drawn at :576-586.
lightningState.channels is a snapshot that only LightningRepo.syncState() refreshes (LightningRepo.kt:1668-1678): at node start (:418), after connectToTrustedPeers() (:1226), after a successful sync() (:756-758), on ChannelPending or ChannelReady (: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, the when at :1364-1385) handles every node event, and none marks a reconnect; ChannelReady fires once per channel. The background sync's SyncCompleted runs WalletRepo.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 to CHANNELS_USABLE_TIMEOUT (:2186, 15 s), and the callers then check canSend() 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 calls syncState().
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 master4d13a1d, 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.
The open feat: add allowances for automatic paykit payments #1340 adds PaykitAllowanceRepo.canPayNow(), which reads lightningState.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, before canSend) and the foreground WalletRepo.syncNodeAndWallet() (:263; its lightningRepo.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-1128 copies lightningService.channels, which only LightningService.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 or CHANNELS_USABLE_TIMEOUT ends, as the empty-list branch already does once. Re-read channels and peers only; syncState() also calls listBalances (see [Bug]: Node.listBalances ANR in receive flow #1036).
While SendSheet shows 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 that waitForUsableChannels() returns well before the timeout.
Refs:
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:hasAnyChannels && 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.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.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.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 afterconnectToTrustedPeers()(LightningRepo.kt:434-441), and a successful sync callssyncState(). 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:
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.
Bitkit Version
Bitkit dev 2.5.0 (190), regtest, 25 Sep 2026. The code cited above was read on
master4d13a1d, 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
canSendcheck; it is the closest earlier sighting.PaykitAllowanceRepo.canPayNow(), which readslightningState.channels.any { it.isUsable }on the same snapshot, so allowance auto-pay also defers while the state is stale.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.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:
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).SendSheetshows the overlay for unusable channels, run the same re-read, so the amount screen offers Spending once the channel is back.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.