aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-05-25 12:56:16 -0700
committerAdam Soltys <[email protected]>2026-05-25 12:56:16 -0700
commitcaebad1dd85188f4315751c6ce77f92d7a1588c2 (patch)
tree74a1e11a68cc88fc7fb59e63cb5830d1cf716893 /lib
parent8aba0bc2e3b5d09b3783b87eb0a29088b40796e4 (diff)
downloadcoinos-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.ts24
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));