To reproduce
- Have onchain balance
- Transfer to spending
- Put app in background during the channel opening process (or have temporary network issues)
Expected
Channel is opened and spending balance is updated.
Actual
Transfer is confirmed, but the channel remains in pending state, spending balance remains 0 in the app indefinitely. Restarting the app resolves the issue.
Logs & Details
bitkit_logs_2026-01-15_11-13-58.zip
Channel details: https://synonymworkspace.slack.com/archives/C0726U60V6Y/p1768473770381519
blocktank-mainnet1 [11:42 AM]
:information_source: info: Created order 929f3d51-d8af-4979-b7c8-c992b316ff26
lspBalanceSat=529124, clientBalanceSat=12226, source=bitkit, discountCode=undefined
[11:42 AM] :information_source: info: Order 929f3d51-d8af-4979-b7c8-c992b316ff26
Onchain tx update
3dad74e164291e95dccb278d4412fb6c743a5cce7d4b0280bf90352563afaa0c:0 14532sat, confirmations: 0, 0conf invalid: rbf.
blocktank-mainnet1 [11:55 AM]
:information_source: info: Order 929f3d51-d8af-4979-b7c8-c992b316ff26
Onchain tx update
3dad74e164291e95dccb278d4412fb6c743a5cce7d4b0280bf90352563afaa0c:0 14532sat, confirmations: 1, 0conf invalid: rbf.
AI Analysis (Claude)
Root Cause
The bug is in TransferViewModel.swift in the watchOrder function. When order polling fails due to a network error, stopPolling() is called and never resumes, even after network connectivity is restored.
Relevant code (TransferViewModel.swift:226-229):
} catch {
Logger.error(error, context: "Failed to watch order")
stopPolling() // BUG: Permanently stops watching on ANY error
}
Timeline from Logs
| Time (UTC) |
Event |
| 10:42:51 |
App creates transfer, starts watching order 929f3d51 |
| 10:52:04 |
App goes to background |
| 10:55:00 |
Blocktank confirms funding tx (1 conf) |
| 10:56:48 |
App returns from background, order poll fails with HTTP error |
| 10:56:48 |
stopPolling() called - order watching stops permanently |
| 10:56:48 |
"The Internet connection appears to be offline" errors |
| 10:58:45 |
LDK node restarts, but order watching never resumes |
| 11:13:52 |
Logs end - app still shows lightning=0, Available channels: 0 |
Key Log Evidence
[2026-01-15 10:56:48.177 UTC] ERROR❌: HTTP client error: error sending request for url (https://api1.blocktank.to/api/channels?ids%5B%5D=929f3d51-d8af-4979-b7c8-c992b316ff26)
[2026-01-15 10:56:48.178 UTC] ERROR❌: Failed to watch order [TransferViewModel.swift: watchOrder(orderId:frequencyMs:) line: 227]
After this error, no further "Refreshing order" logs appear, confirming polling stopped.
Contributing Factor
The Lightning channel handshake also requires the LDK node to be online. The app was going in/out of background with network instability, preventing the channel opening handshake from completing until the user manually restarted the app.
Recommended Fix
- Don't stop polling on transient errors - Implement retry logic with exponential backoff instead of calling
stopPolling() on any error:
} catch {
Logger.error(error, context: "Failed to watch order, will retry")
// Don't call stopPolling() - let the timer continue
// Optionally track error count and only stop after N consecutive failures
}
- Resume watching pending transfers on app foreground - When the app returns to foreground, check for any pending
toSpending transfers and resume watching them:
// In ScenePhase or AppViewModel when app becomes active
if let pendingTransfer = transferService.getPendingToSpendingTransfer() {
watchOrder(orderId: pendingTransfer.orderId)
}
- Consider adding a "refresh" button - Allow users to manually trigger a sync of pending transfers if automatic recovery fails.
To reproduce
Expected
Channel is opened and spending balance is updated.
Actual
Transfer is confirmed, but the channel remains in pending state, spending balance remains 0 in the app indefinitely. Restarting the app resolves the issue.
Logs & Details
bitkit_logs_2026-01-15_11-13-58.zip
Channel details: https://synonymworkspace.slack.com/archives/C0726U60V6Y/p1768473770381519
AI Analysis (Claude)
Root Cause
The bug is in
TransferViewModel.swiftin thewatchOrderfunction. When order polling fails due to a network error,stopPolling()is called and never resumes, even after network connectivity is restored.Relevant code (
TransferViewModel.swift:226-229):Timeline from Logs
929f3d51stopPolling()called - order watching stops permanentlylightning=0,Available channels: 0Key Log Evidence
After this error, no further "Refreshing order" logs appear, confirming polling stopped.
Contributing Factor
The Lightning channel handshake also requires the LDK node to be online. The app was going in/out of background with network instability, preventing the channel opening handshake from completing until the user manually restarted the app.
Recommended Fix
stopPolling()on any error:toSpendingtransfers and resume watching them: