diff --git a/core/archipelago/src/api/rpc/lnd/wallet.rs b/core/archipelago/src/api/rpc/lnd/wallet.rs index d679de7e..8997c998 100644 --- a/core/archipelago/src/api/rpc/lnd/wallet.rs +++ b/core/archipelago/src/api/rpc/lnd/wallet.rs @@ -192,6 +192,19 @@ impl RpcHandler { .get("message") .and_then(|v| v.as_str()) .unwrap_or("Unknown error"); + // LND's sweep refusal reads like a debug dump ("insufficient + // input to create sweep tx: input_sum=0 BTC, output_sum=…"). + // input_sum=0 with a tiny output means the wallet's coins are + // unconfirmed or below Bitcoin's dust minimum — say that + // (framework-pt sweep of 92 sats, 2026-08-06). + if msg.contains("insufficient input to create sweep tx") { + return Err(anyhow::anyhow!( + "Failed to send: your on-chain balance is too small or still \ + unconfirmed to sweep. Bitcoin cannot build a transaction from \ + coins below the dust minimum (~546 sats) or from funds that \ + have not confirmed yet. (LND: {msg})" + )); + } return Err(anyhow::anyhow!("Failed to send: {}", msg)); } diff --git a/core/archipelago/src/api/rpc/middleware.rs b/core/archipelago/src/api/rpc/middleware.rs index a20a184f..bba77598 100644 --- a/core/archipelago/src/api/rpc/middleware.rs +++ b/core/archipelago/src/api/rpc/middleware.rs @@ -79,6 +79,11 @@ pub(super) fn sanitize_error_message(msg: &str) -> String { // them in the first place (ecash send, 2026-07-22). "Insufficient balance", "Insufficient funds", + // On-chain send/sweep refusals from LND ("Failed to send: your + // on-chain balance is too small or still unconfirmed to sweep…"). + // Masking sent the operator to journalctl again (framework-pt + // sweep, 2026-08-06) — same lesson as the two above. + "Failed to send", // Lightning payment failures carry LND's reason ("invoice expired. // Valid until …", "no route", …) — the user can act on every one of // them, and masking sent the operator to journalctl (invoice-expired