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
{{ message }}
Repository navigation
crash: native abort when the node restarts after returning from the background #1536
Bitkit Android crashed with a native SIGABRT when the Lightning node restarted after the app returned from the background to the foreground:
A Tokio 1.x context was found, but it is being shutdown.
Seen by @jvsena42 on master (14:48:36 UTC, 16 Oct) during the device check of #1532 (#1532 (comment)), outside that PR's change. It looks like work still being scheduled on the node's Tokio runtime while that runtime shuts down during the restart, the same family as the teardown crash fixed in synonymdev/ldk-node#111.
Expected behavior
Restarting the node on resume never crashes the app: work started by the old runtime is cancelled or finished before it shuts down, and nothing is scheduled on a runtime that is shutting down.
Steps to Reproduce
Open Bitkit Android with a running node.
Send it to the background long enough for the node to stop, then bring it back so the node restarts.
Occasionally the app aborts with the message above.
Android vitals (Play Console, last 60 days) show the same panic, A Tokio 1.x context was found, but it is being shutdown., in released builds:
The app's top crash cluster, native SIGABRT in split_config.arm64_v8a.apk: 114 events, 30 users, versions 181 (2.2.0), 187 (2.4.0), 188 (2.4.1), 190 (2.5.0) and 191 (2.5.1). The 2.4.1 sample (Pixel 7, Android 16) carries this message; that build's stack is unsymbolicated.
Two symbolicated 2.5.1 (191) issues with this message: 1 user and 2 events (20 hours ago), and 1 user and 1 event (3 days ago), both on Pixel 7, Android 16, foreground.
The 2.5.1 stack locates the panic in ldk-node: a periodic task spawned by Node::start (RuntimeControl::spawn_tracked_task) polls tokio::time::Interval::tick while the runtime shuts down, from a blocking task in RuntimeControl::try_block_on:
Seen again on RC3 (3.0.0 build 194, 6e4bc01f5, ldk-node 0.7.0-rc.76), devDebug regtest on an Android 16 emulator, 19 Oct.
Verdict: this crash existed before 3.0.0 and is not an rc.76 regression. Play Vitals shows the same panic and the same Interval::tick inside try_block_on/block_in_place stack in 2.4.1 (188) and 2.5.1 (191): #1536 (comment). The emulator runs below only compare how often rc.74 and rc.76 hit it under the same recipe. Neither reproduced it, so they show no rise with rc.76.
Symbolicated stack
Abort message: 'A Tokio 1.x context was found, but it is being shutdown.', tid 17443 (Thread-894), process uptime 4088 s. Every app frame is in libldk_node.so (BuildId 52659fe1, a local rc.76 build; see below). There is no JNI frame: the abort happened on a worker thread of the old node's own Tokio runtime.
The crashing APK was a local devDebug build whose ldk-node came from a locally built rc.76 in ~/.m2 (BuildId 52659fe1), not the published rc.76 AAR (ee23b4f6). The stack above is symbolicated exactly against that local build's unstripped libldk_node.so with llvm-symbolizer. ldk-node's published AAR and its native-debug-symbols match each other, and so do the 3.0.1 (195) release artifacts. #1574 makes mavenLocal() opt-in so local QA builds use the published libraries.
What the stack shows:
The chain-sync loop is a tracked background task.
It was polled by a worker core that had been handed to a blocking-pool thread. The handoff happened when a VssStore sync call went through RuntimeControl::try_block_on → block_in_place (src/runtime.rs:299-306).
The loop polled its Interval after the old runtime's time driver had shut down, which is what makes the timer assert fire.
Timeline (emulator logcat + app log, CEST)
10:00:01.166 LightningService: Stopping node… (app backgrounded, network already down)
10:00:06.360 ERROR ldk_node::runtime:356 Stopping background tasks timed out.
10:00:11.363 ERROR ldk_node::runtime:362 Detaching background tasks after cancellation timed out.
10:00:41.377 ERROR ldk_node::runtime:397 Stopping event handling timed out: deadline has elapsed
10:00:42.388 WARN LightningService.kt:538 Node handle release still pending after 1s
10:01:05.865 ActivityManager: freezing 15604 to.bitkit.dev
11:06:15.849 ActivityManager: sync unfroze 15604 to.bitkit.dev (Shop link brought Bitkit back)
11:06:16.833 LightningService.kt:250 Building node…
11:06:16.968 F libc: Fatal signal 6 (SIGABRT) in tid 17443 (Thread-894)
The stop never logged Stopping background syncing on-chain wallet., so the chain-sync loop was the tracked task that got detached.
Thread 17443 is the old runtime's worker: it logged that node's fee-rate updates at 10:00:00.
The abort comes about 1.1 s after the process thaws and before the new node starts. No Kotlin call is involved.
Thawing the frozen process let the detached task of the old runtime run again. The runtime had already started shutting down through node.destroy() → Runtime::drop → shutdown_background() (src/runtime.rs:88-97).
So any resume works as a trigger (here a Shop checkout link). The checkout itself plays no part.
Not the rc.76 Electrum reconnect
git diff v0.7.0-rc.74 v0.7.0-rc.76 changes only src/chain/electrum.rs (plus version files).
src/runtime.rs, src/chain/mod.rs, src/lib.rs and src/io/vss_store.rs are identical between the two tags.
The new reconnect_main_client closure (spawn_blocking) is synchronous and never creates or polls a timer.
None of the crashing frames are in electrum.rs.
This matches the first sighting on master with rc.74 (16 Oct, #1532 (comment)).
Repro on emulators
Recipe:
Fresh regtest wallet on a new API 35 google_apis emulator.
Node running, then an iptables DROP on VSS/RGS (34.65.252.32:443) for 30 s while the app is in the foreground. Electrum stays up.
Home → the node stop times out.
SIGSTOP the process 8 s after Node stopped, standing in for the freezer. The API 35 image didn't freeze the app on its own.
After 150-240 s: restore the network, SIGCONT, and bring the app back from the launcher.
This reproduces the stop on 5654 exactly. All three lines appeared on every run:
Stopping background tasks timed out
Detaching background tasks after cancellation timed out
Stopping event handling timed out
Node handle release still pending followed each time. While VSS is blackholed, the chain-sync loop is stuck in a VSS write (//node_metrics) through block_in_place until the VSS client's 180 s retry budget runs out (src/io/vss_store.rs:693-697). That's why it ignores the stop.
Build
ldk-node
Runs
Stop timed out (all 3 lines)
Freeze/thaw + resume
Abort
RC2 193 (19449fb0d)
rc.74
5
5
3 (SIGSTOP 150-240 s)
0
RC3 194 (6e4bc01f5)
rc.76
3
3
3 (SIGSTOP 200 s)
0
Two of the RC2 runs had no freeze: one used airplane mode on an API 36.1 image with real freezing, and one used the VSS drop. In those, the stuck loop finished by itself after the retry budget ran out and logged Stopping background syncing on-chain wallet..
On 5654 the process stayed frozen for 65 minutes, and the abort came 1.1 s after the thaw. A freeze of a few minutes wasn't enough to hit it, which fits the low rate in production. 0/3 against 0/3 says nothing about the rate either way. The code path and the Vitals data are what show the crash predates 3.0.0.
iOS
Same exposure in the code, but iOS reaches it less often on resume.
bitkit-ios master (7b3f093b) pins rc.76 (Package.resolved), so it has the same runtime.rs, VSS block_in_place and chain-sync loop.
iOS doesn't stop the node when the app goes to the background. AppScene.handleScenePhaseChange (Bitkit/AppScene.swift:1250-1290) only pauses Paykit, locks the PIN and notifies the hardware wallet on .background. On .active it calls nodeRestarter.retryOnForeground, which restarts only a node in errorStarting (Bitkit/Utilities/NodeRestarter.swift:34-41). A suspended app therefore resumes its one live runtime, with no timed-out stop and no runtime that has already shut down.
restart on recovery or network restore (Bitkit/AppScene.swift:2006, NodeRestarter.swift:61)
LightningService.stop() (Bitkit/Services/LightningService.swift:300-320) sets node = nil straight after node.stop(). The Runtime drop and shutdown_background() then run there. A stop that times out with work still stuck in block_in_place would leave the same detached task behind on iOS too.
The mechanism lives in ldk-node and doesn't depend on the app version:
a tracked task that ignores the stop because it's stuck in block_in_place
detached on timeout
the runtime shut down underneath it
The fix belongs in ldk-node. A task left detached after a timed-out stop must never be polled once its runtime's time driver has shut down. Today the timer asserts (tokio-1.53.1/src/runtime/time/entry.rs:539) and panic = "abort" turns that into the SIGABRT.
This adds current-release evidence to the already tracked shutdown failure. The new sample's native frames are unsymbolicated; it does not independently establish the exact task/lock sequence discussed above.
Across all versions the same cluster shows 18 events / 7 users in this 28-day window. Those totals include older releases and must not be described as this week's or version 195's totals.
Additional external device evidence from João's October 28 check of #1686: the same A Tokio 1.x context was found, but it is being shutdown. SIGABRT was observed while backgrounding with VSS unreachable, Electrum available and a SETTINGS backup pending.
Reported controlled comparison on a Pixel_9 emulator with the cached-apps freezer disabled:
The reviewer reports byte-identical libldk_node.so (BuildId prefix 7224179a…), the abort 2–4 ms after a failed //node_metrics write, and matching 39-frame traces apart from one offset. This is attributed reviewer evidence: I inspected the complete posted comparison, but not its raw APKs, tombstones or full logs. These small samples do not establish a rate difference or independently prove an exact task/lock cause.
Tracking here instead of opening a duplicate or treating the failure as introduced by #1686's profile-restore change. The existing shutdown/VSS investigation and native symbolication requirements remain applicable; no unchanged CI rerun or recovery/spending gate relaxation follows from this report.
Additional production evidence from the 2 November 2026 Play Console review. 3.0.1 (195) now has 8 user-perceived crash events across four clusters matching this shutdown panic. These are rolling-window counts, not weekly totals, and affected users must not be summed across clusters.
Window: 5 October–2 November 2026; source updated Sunday, 8:00 AM (timezone not displayed). The current production release is still 3.0.1 (195), released 21 October at 7:47 AM as displayed.
The last three cluster IDs were absent from the 26 October review. All three inspected samples are Google Pixel 7 / Android 16 / build 195, foreground, with the explicit message:
A Tokio 1.x context was found, but it is being shutdown.
BuildId: ee23b4f6171a3eb8317e265d94b01b3a41eed556
Their symbolicated frames include Sleep::poll → Interval::poll_tick / tick → ldk-node tracked tasks. Cluster 8f3c… includes ChainSource::start_tx_based_sync_loop, Node::spawn_chain_sync_task_with_receiver, and RuntimeControl::try_block_on / block_in_place. Clusters 132d… and dcb9… include Node::start and RuntimeControl::spawn_tracked_task.
These signatures match the existing shutdown investigation, so no duplicate issues were opened. This does not establish a new root cause or a crash-rate increase: production crash rates remain unavailable, and the reporting windows overlap.
Production recurrence in the 9 November 2026 Play Console review, on 3.0.1 (195). Three existing symbolicated clusters each gained one event since the 2 November review:
The fourth mapped cluster, 025a57b3db54dc722bb0cbed27ed8e12, remains at 4 events / 3 users on build 195. Together the four clusters show 11 events on 195, compared with 8 in the previous review. Do not sum affected users across clusters.
All three inspected detail views still identify Google Pixel 7 / Android 16 / foreground / build 195, with A Tokio 1.x context was found, but it is being shutdown. and native BuildId ee23b4f6171a3eb8317e265d94b01b3a41eed556. The available 8f3c… and dcb9… samples are dated 4 and 6 days ago; the 132d… sample is 8 days old even though its aggregate last occurrence is 2 days ago. The former retains ChainSource::start_tx_based_sync_loop and tracked-task frames; the latter retains Node::start / RuntimeControl::spawn_tracked_task. No distinct failure signature or new root cause is established.
Current source window: 12 October–9 November, user-perceived crashes/ANRs, build 195; data updated Sunday 8 November, 9:00 AM as displayed, timezone unstated. These overlapping rolling-window counts are not weekly totals or crash rates.
3.1.0 (196) was released today (Play displays 9 November, 8:31 AM). Its release dashboard reports stability data unavailable, and build 196 is not yet offered in the vitals version filter. This evidence does not establish recurrence or resolution on 3.1.0.
Proposed handling, given the Breez/Spark migration
ldk-node is still in the app through the migration. feat/move-to-spark (#1774) keeps ldk-node-android 0.7.0-rc.79 alongside breez-sdk-spark, Move to Spark pays over the LDK channels and then closes them, and ldk-node is still the on-chain wallet until a BDK replacement lands. So every user still runs the crashing code path. A proper fix in ldk-node's runtime and VSS code is a lot of work for code we plan to remove, so I propose a cheap app-side mitigation instead.
1. End the process after a timed-out stop (now)
When the node stop times out in the background (Stopping background tasks timed out / Node handle release still pending), end the process instead of keeping it alive. The detached task on the old runtime can then never be thawed and polled, and the next launch starts clean. The change is small and lives in LightningService.kt. It doesn't depend on the migration.
2. Stop starting ldk-node once it has nothing left to resolve (after on-chain moves to BDK)
"No channels" is not enough. A closed channel can still need the node:
coop close not yet buried (reorg)
force close: CSV-delayed to_self output and HTLC claims/timeouts are swept by the node
a revoked state broadcast before the funding output is spent for good needs the justice tx
Gate on all of these after a full sync:
listChannels() is empty
listBalances().lightningBalances is empty (no ChannelMonitor with anything claimable or awaiting confirmations)
listBalances().pendingBalancesFromChannelClosures is empty (nothing left in the sweeper)
Save that as a flag and skip starting the node while it is set. Clear the flag on anything that could bring LDK state back, such as a restore from VSS.
Caveats:
The node has to run at least once after the last close resolves, so users who rarely open the app keep running it longer.
This only works once on-chain is off ldk-node. Close and sweep outputs land in ldk-node's internal wallet, so the BDK wallet must see them (same seed and derivation, or a one-time sweep) before the node stops starting.
3. Keep this issue open as known
Close it when ldk-node is removed. Revisit if the BDK move slips, or if 3.1.0 (196) vitals show the crash rate going up.
Activity
ovitrif commentedon Oct 19, 2026
Production data: this crash predates 3.0.0
Android vitals (Play Console, last 60 days) show the same panic,
A Tokio 1.x context was found, but it is being shutdown., in released builds:SIGABRTinsplit_config.arm64_v8a.apk: 114 events, 30 users, versions 181 (2.2.0), 187 (2.4.0), 188 (2.4.1), 190 (2.5.0) and 191 (2.5.1). The 2.4.1 sample (Pixel 7, Android 16) carries this message; that build's stack is unsymbolicated.The 2.5.1 stack locates the panic in ldk-node: a periodic task spawned by
Node::start(RuntimeControl::spawn_tracked_task) pollstokio::time::Interval::tickwhile the runtime shuts down, from a blocking task inRuntimeControl::try_block_on:So this is a long-standing ldk-node shutdown race, not a regression from the 3.0.0 changes (rc.74 → rc.76).
ovitrif commentedon Oct 19, 2026
Seen again on RC3 (3.0.0 build 194,
6e4bc01f5, ldk-node 0.7.0-rc.76), devDebug regtest on an Android 16 emulator, 19 Oct.Verdict: this crash existed before 3.0.0 and is not an rc.76 regression. Play Vitals shows the same panic and the same
Interval::tickinsidetry_block_on/block_in_placestack in 2.4.1 (188) and 2.5.1 (191): #1536 (comment). The emulator runs below only compare how often rc.74 and rc.76 hit it under the same recipe. Neither reproduced it, so they show no rise with rc.76.Symbolicated stack
Abort message: 'A Tokio 1.x context was found, but it is being shutdown.', tid 17443 (Thread-894), process uptime 4088 s. Every app frame is inlibldk_node.so(BuildId52659fe1, a local rc.76 build; see below). There is no JNI frame: the abort happened on a worker thread of the old node's own Tokio runtime.The crashing APK was a local devDebug build whose ldk-node came from a locally built rc.76 in
~/.m2(BuildId52659fe1), not the published rc.76 AAR (ee23b4f6). The stack above is symbolicated exactly against that local build's unstrippedlibldk_node.sowithllvm-symbolizer. ldk-node's published AAR and itsnative-debug-symbolsmatch each other, and so do the 3.0.1 (195) release artifacts. #1574 makesmavenLocal()opt-in so local QA builds use the published libraries.What the stack shows:
VssStoresync call went throughRuntimeControl::try_block_on→block_in_place(src/runtime.rs:299-306).Intervalafter the old runtime's time driver had shut down, which is what makes the timer assert fire.Timeline (emulator logcat + app log, CEST)
Stopping background syncing on-chain wallet., so the chain-sync loop was the tracked task that got detached.node.destroy()→Runtime::drop→shutdown_background()(src/runtime.rs:88-97).Not the rc.76 Electrum reconnect
git diff v0.7.0-rc.74 v0.7.0-rc.76changes onlysrc/chain/electrum.rs(plus version files).src/runtime.rs,src/chain/mod.rs,src/lib.rsandsrc/io/vss_store.rsare identical between the two tags.reconnect_main_clientclosure (spawn_blocking) is synchronous and never creates or polls a timer.electrum.rs.This matches the first sighting on master with rc.74 (16 Oct, #1532 (comment)).
Repro on emulators
Recipe:
google_apisemulator.iptablesDROP on VSS/RGS (34.65.252.32:443) for 30 s while the app is in the foreground. Electrum stays up.Node stopped, standing in for the freezer. The API 35 image didn't freeze the app on its own.This reproduces the stop on 5654 exactly. All three lines appeared on every run:
Stopping background tasks timed outDetaching background tasks after cancellation timed outStopping event handling timed outNode handle release still pendingfollowed each time. While VSS is blackholed, the chain-sync loop is stuck in a VSS write (//node_metrics) throughblock_in_placeuntil the VSS client's 180 s retry budget runs out (src/io/vss_store.rs:693-697). That's why it ignores the stop.19449fb0d)6e4bc01f5)Two of the RC2 runs had no freeze: one used airplane mode on an API 36.1 image with real freezing, and one used the VSS drop. In those, the stuck loop finished by itself after the retry budget ran out and logged
Stopping background syncing on-chain wallet..On 5654 the process stayed frozen for 65 minutes, and the abort came 1.1 s after the thaw. A freeze of a few minutes wasn't enough to hit it, which fits the low rate in production. 0/3 against 0/3 says nothing about the rate either way. The code path and the Vitals data are what show the crash predates 3.0.0.
iOS
Same exposure in the code, but iOS reaches it less often on resume.
master(7b3f093b) pins rc.76 (Package.resolved), so it has the sameruntime.rs, VSSblock_in_placeand chain-sync loop.AppScene.handleScenePhaseChange(Bitkit/AppScene.swift:1250-1290) only pauses Paykit, locks the PIN and notifies the hardware wallet on.background. On.activeit callsnodeRestarter.retryOnForeground, which restarts only a node inerrorStarting(Bitkit/Utilities/NodeRestarter.swift:34-41). A suspended app therefore resumes its one live runtime, with no timed-out stop and no runtime that has already shut down.Bitkit/ViewModels/WalletViewModel.swift:1171):1885)Bitkit/AppScene.swift:2006,NodeRestarter.swift:61)LightningService.stop()(Bitkit/Services/LightningService.swift:300-320) setsnode = nilstraight afternode.stop(). TheRuntimedrop andshutdown_background()then run there. A stop that times out with work still stuck inblock_in_placewould leave the same detached task behind on iOS too.The mechanism lives in ldk-node and doesn't depend on the app version:
block_in_placeThe fix belongs in ldk-node. A task left detached after a timed-out stop must never be polled once its runtime's time driver has shut down. Today the timer asserts (
tokio-1.53.1/src/runtime/time/entry.rs:539) andpanic = "abort"turns that into the SIGABRT.piotr-iohk commentedon Oct 26, 2026
Additional production evidence from the Oct 26, 2026 Play Console review:
A Tokio 1.x context was found, but it is being shutdown.ee23b4f6171a3eb8317e265d94b01b3a41eed556.This adds current-release evidence to the already tracked shutdown failure. The new sample's native frames are unsymbolicated; it does not independently establish the exact task/lock sequence discussed above.
Across all versions the same cluster shows 18 events / 7 users in this 28-day window. Those totals include older releases and must not be described as this week's or version 195's totals.
ben-kaufman commentedon Oct 28, 2026
Additional external device evidence from João's October 28 check of #1686: the same
A Tokio 1.x context was found, but it is being shutdown.SIGABRT was observed while backgrounding with VSS unreachable, Electrum available and a SETTINGS backup pending.Reported controlled comparison on a Pixel_9 emulator with the cached-apps freezer disabled:
ea89ed10fd5e424The reviewer reports byte-identical
libldk_node.so(BuildId prefix7224179a…), the abort 2–4 ms after a failed//node_metricswrite, and matching 39-frame traces apart from one offset. This is attributed reviewer evidence: I inspected the complete posted comparison, but not its raw APKs, tombstones or full logs. These small samples do not establish a rate difference or independently prove an exact task/lock cause.Tracking here instead of opening a duplicate or treating the failure as introduced by #1686's profile-restore change. The existing shutdown/VSS investigation and native symbolication requirements remain applicable; no unchanged CI rerun or recovery/spending gate relaxation follows from this report.
piotr-iohk commentedon Nov 2, 2026
Additional production evidence from the 2 November 2026 Play Console review. 3.0.1 (195) now has 8 user-perceived crash events across four clusters matching this shutdown panic. These are rolling-window counts, not weekly totals, and affected users must not be summed across clusters.
Window: 5 October–2 November 2026; source updated Sunday, 8:00 AM (timezone not displayed). The current production release is still 3.0.1 (195), released 21 October at 7:47 AM as displayed.
The last three cluster IDs were absent from the 26 October review. All three inspected samples are Google Pixel 7 / Android 16 / build 195, foreground, with the explicit message:
Their symbolicated frames include
Sleep::poll→Interval::poll_tick/tick→ ldk-node tracked tasks. Cluster8f3c…includesChainSource::start_tx_based_sync_loop,Node::spawn_chain_sync_task_with_receiver, andRuntimeControl::try_block_on/block_in_place. Clusters132d…anddcb9…includeNode::startandRuntimeControl::spawn_tracked_task.These signatures match the existing shutdown investigation, so no duplicate issues were opened. This does not establish a new root cause or a crash-rate increase: production crash rates remain unavailable, and the reporting windows overlap.
piotr-iohk commentedon Nov 9, 2026
Production recurrence in the 9 November 2026 Play Console review, on 3.0.1 (195). Three existing symbolicated clusters each gained one event since the 2 November review:
The fourth mapped cluster,
025a57b3db54dc722bb0cbed27ed8e12, remains at 4 events / 3 users on build 195. Together the four clusters show 11 events on 195, compared with 8 in the previous review. Do not sum affected users across clusters.All three inspected detail views still identify Google Pixel 7 / Android 16 / foreground / build 195, with
A Tokio 1.x context was found, but it is being shutdown.and native BuildIdee23b4f6171a3eb8317e265d94b01b3a41eed556. The available8f3c…anddcb9…samples are dated 4 and 6 days ago; the132d…sample is 8 days old even though its aggregate last occurrence is 2 days ago. The former retainsChainSource::start_tx_based_sync_loopand tracked-task frames; the latter retainsNode::start/RuntimeControl::spawn_tracked_task. No distinct failure signature or new root cause is established.Current source window: 12 October–9 November, user-perceived crashes/ANRs, build 195; data updated Sunday 8 November, 9:00 AM as displayed, timezone unstated. These overlapping rolling-window counts are not weekly totals or crash rates.
3.1.0 (196) was released today (Play displays 9 November, 8:31 AM). Its release dashboard reports stability data unavailable, and build 196 is not yet offered in the vitals version filter. This evidence does not establish recurrence or resolution on 3.1.0.
jvsena42 commentedon Nov 10, 2026
Proposed handling, given the Breez/Spark migration
ldk-node is still in the app through the migration.
feat/move-to-spark(#1774) keepsldk-node-android 0.7.0-rc.79alongsidebreez-sdk-spark, Move to Spark pays over the LDK channels and then closes them, and ldk-node is still the on-chain wallet until a BDK replacement lands. So every user still runs the crashing code path. A proper fix in ldk-node's runtime and VSS code is a lot of work for code we plan to remove, so I propose a cheap app-side mitigation instead.1. End the process after a timed-out stop (now)
When the node stop times out in the background (
Stopping background tasks timed out/Node handle release still pending), end the process instead of keeping it alive. The detached task on the old runtime can then never be thawed and polled, and the next launch starts clean. The change is small and lives inLightningService.kt. It doesn't depend on the migration.2. Stop starting ldk-node once it has nothing left to resolve (after on-chain moves to BDK)
"No channels" is not enough. A closed channel can still need the node:
to_selfoutput and HTLC claims/timeouts are swept by the nodeGate on all of these after a full sync:
listChannels()is emptylistBalances().lightningBalancesis empty (noChannelMonitorwith anything claimable or awaiting confirmations)listBalances().pendingBalancesFromChannelClosuresis empty (nothing left in the sweeper)Save that as a flag and skip starting the node while it is set. Clear the flag on anything that could bring LDK state back, such as a restore from VSS.
Caveats:
3. Keep this issue open as known
Close it when ldk-node is removed. Revisit if the BDK move slips, or if 3.1.0 (196) vitals show the crash rate going up.