Follow-up to #88 (closed). #88 established that the LSP peer reconnect is paid lazily by the next operation that needs the peer, and that the receive/JIT path triggers it. This is the corollary from the send side: pay() never triggers that reconnect, so a Lightning send during the reconnect window fails RouteNotFound with zero first hops even though the internet is already back.
The asymmetry (orange-sdk 5a540f6, ldk-node 0cea341)
Receive connects. LightningWallet::get_bolt11_invoice → receive_via_jit_channel → ldk-node receive_via_jit_channel_inner hands connection_manager to the LSPS2 client (lsps2_receive_to_jit_channel(…, connection_manager, …)), which connects to the LSP as part of negotiating the JIT channel.
Send does not. LightningWallet::pay (Bolt11) → ldk_node.bolt11_payment().send_using_amount() → send_internal → send_payment. No connection_manager, no connect. send_payment routes over list_usable_channels(), and a channel is only usable while its peer is connected — so with the LSP peer down the router gets zero first hops and returns RouteNotFound immediately. Nothing re-establishes the connection; the send just waits for ldk-node's background reconnect loop to catch up.
Field incident (2026-07-25, mobile, staging)
Network flapped; internet returned in ~2 s, but the Lightning peer stayed down for ~48 s. During that window a Lightning send failed 6× RouteNotFound in ~25 s (same hash, zero HTLCs dispatched) — while the mint (HTTPS) was reachable the whole time. A receive during the same window would have reconnected the peer immediately.
Not an edge case: under a small-trusted-float model most sends from a funded wallet exceed the float and route over Lightning, so they're peer-dependent — a connectivity flap then reliably produces failing sends until the background reconnect lands.
Proposed direction
Before dispatching a Lightning send, ensure the LSP peer is connected — mirroring what the JIT-receive path already does. is_connected_to_lsp() and lsp_node_id are already on hand, so it's a small guard in pay() (if !is_connected_to_lsp() { connect(…) }).
The one call that's yours to make: block the send until the reconnect completes, or best-effort with a short timeout. Happy to open a PR once you're comfortable with the direction — we have an on-device harness to validate against.
Follow-up to #88 (closed). #88 established that the LSP peer reconnect is paid lazily by the next operation that needs the peer, and that the receive/JIT path triggers it. This is the corollary from the send side:
pay()never triggers that reconnect, so a Lightning send during the reconnect window failsRouteNotFoundwith zero first hops even though the internet is already back.The asymmetry (orange-sdk
5a540f6, ldk-node0cea341)Receive connects.
LightningWallet::get_bolt11_invoice→receive_via_jit_channel→ ldk-nodereceive_via_jit_channel_innerhandsconnection_managerto the LSPS2 client (lsps2_receive_to_jit_channel(…, connection_manager, …)), which connects to the LSP as part of negotiating the JIT channel.Send does not.
LightningWallet::pay(Bolt11) →ldk_node.bolt11_payment().send_using_amount()→send_internal→send_payment. Noconnection_manager, no connect.send_paymentroutes overlist_usable_channels(), and a channel is only usable while its peer is connected — so with the LSP peer down the router gets zero first hops and returnsRouteNotFoundimmediately. Nothing re-establishes the connection; the send just waits for ldk-node's background reconnect loop to catch up.Field incident (2026-07-25, mobile, staging)
Network flapped; internet returned in ~2 s, but the Lightning peer stayed down for ~48 s. During that window a Lightning send failed 6×
RouteNotFoundin ~25 s (same hash, zero HTLCs dispatched) — while the mint (HTTPS) was reachable the whole time. A receive during the same window would have reconnected the peer immediately.Not an edge case: under a small-trusted-float model most sends from a funded wallet exceed the float and route over Lightning, so they're peer-dependent — a connectivity flap then reliably produces failing sends until the background reconnect lands.
Proposed direction
Before dispatching a Lightning send, ensure the LSP peer is connected — mirroring what the JIT-receive path already does.
is_connected_to_lsp()andlsp_node_idare already on hand, so it's a small guard inpay()(if !is_connected_to_lsp() { connect(…) }).The one call that's yours to make: block the send until the reconnect completes, or best-effort with a short timeout. Happy to open a PR once you're comfortable with the direction — we have an on-device harness to validate against.