diff options
| author | Adam Soltys <[email protected]> | 2026-05-25 12:56:16 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-05-25 12:56:16 -0700 |
| commit | caebad1dd85188f4315751c6ce77f92d7a1588c2 (patch) | |
| tree | 74a1e11a68cc88fc7fb59e63cb5830d1cf716893 /lib | |
| parent | 8aba0bc2e3b5d09b3783b87eb0a29088b40796e4 (diff) | |
| download | coinos-server-caebad1dd85188f4315751c6ce77f92d7a1588c2.tar.gz coinos-server-caebad1dd85188f4315751c6ce77f92d7a1588c2.zip | |
sendLightning: stop warning on healthy multi-path payments
Each xpay with failed_parts > 0 that ultimately settled (multi-path
retry — common, not concerning) was firing 3 WARN lines:
"xpay returned (abnormal)"
"xpay returned failed_parts without throwing"
"xpay failed_parts but listpays complete — finalizing"
These are all the SAME successful payment. The presence of failed_parts
just means CLN tried multiple paths before one succeeded. Adam was
seeing noise.
Now:
- Only warn on actually-missing preimage (xpay silently failed).
- Drop the unconditional "without throwing" warn — the failed_parts
branch handles the recovery silently in the success case.
- Drop the "listpays complete — finalizing" warn — that's the success
path, not a concern.
- KEEP warns for the genuinely interesting cases: listpays says failed
(we're about to reverse), listpays says pending (debit left for
check() to reconcile), or finalize itself throws.
Net effect: a healthy multi-path payment now emits only the standard
"sendLightning outcome <id> = finalized-via-listpays-after-failed_parts"
info line instead of 3 WARN lines.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/payments.ts | 24 |
1 files changed, 13 insertions, 11 deletions
diff --git a/lib/payments.ts b/lib/payments.ts index ffd3ddc1..96469534 100644 --- a/lib/payments.ts +++ b/lib/payments.ts @@ -707,36 +707,38 @@ export const sendLightning = async ({ layers: ["prefer-kappa"], }); - // Only log the xpay response shape when there's something interesting — - // failed_parts > 0 or a missing preimage. The success case is noise. - if (r?.failed_parts || !(r?.payment_preimage ?? r?.preimage)) { - warn("xpay returned (abnormal)", p.id, JSON.stringify({ + // Only log the xpay response when there's a real concern — no preimage + // at all (xpay failed silently and we'll need to reverse) is interesting; + // failed_parts WITH a preimage is normal multi-path retry behavior and + // not worth warning about. The failed_parts branch below handles the + // recovery without log spam in the success case. + if (!(r?.payment_preimage ?? r?.preimage)) { + warn("xpay returned no preimage", p.id, JSON.stringify({ status: r?.status, failed_parts: r?.failed_parts, parts: r?.parts, - has_preimage: !!(r?.payment_preimage ?? r?.preimage), amount_sent_msat: r?.amount_sent_msat, amount_msat: r?.amount_msat, })); } if (r.failed_parts) { - // xpay didn't throw but reported failed parts. The old code path - // skipped finalize and left the debit stranded. Verify with listpays - // and act accordingly. - warn("xpay returned failed_parts without throwing", p.id, r.failed_parts); + // xpay didn't throw but reported failed parts. Verify with listpays + // before deciding — for multi-path payments this is the common path + // and usually finalizes cleanly; the noisy warns only fire when + // listpays disagrees with xpay or finalize itself throws. try { const { pays } = await ln.listpays(pr); const completed = pays.find((x) => x.status === "complete"); const failed = !pays.length || pays.every((x) => x.status === "failed"); if (completed) { - warn("xpay failed_parts but listpays complete — finalizing", p.id); try { await finalize(completed, p); outcome = "finalized-via-listpays-after-failed_parts"; } catch (e: any) { warn("finalize threw in failed_parts branch", p.id, e?.message); outcome = "finalize-threw-failed_parts"; } } else if (failed) { warn("xpay failed_parts and listpays failed — reversing", p.id); await reverse(p); outcome = "reversed-via-failed_parts"; } else { - outcome = "pending-after-failed_parts"; // leave for check() loop + warn("xpay failed_parts, listpays still pending — leaving for check()", p.id); + outcome = "pending-after-failed_parts"; } } catch (verifyErr: any) { warnThrottled("listpays verification failed (failed_parts branch)", verifyErr?.message ?? String(verifyErr)); |
