xpay: return the correct (documented!) error code on repeat payments.
What changed, and why it matters
This is a tiny bug fix in the Core Lightning 'xpay' plugin. When a user tried to pay an invoice that had already been paid, the plugin was returning the wrong numeric error code (218 instead of the documented 219). The change makes the returned code match the documentation and the test now checks the exact code. It does not create a security vulnerability or allow anyone to steal funds; it only affects the error message details returned to API callers.
No security action required. Treat as a normal correctness/documentation fix. Optionally review whether other xpay error-code mappings match the man page.
Security signals we found
No security-relevant code path altered
Error-code correction only; no privilege, authentication, or payment validity change
Test coverage added for documented behavior
Evidence from the diff
In plugins/xpay/xpay.c, update_knowledge_from_error() previously mapped PAY_INJECTPAYMENTONION_ALREADY_PAID to PAY_INJECTPAYMENTONION_FAILED (218) when giving up on a duplicate payment. The patch maps it to PAY_INJECTPAYMENTONION_ALREADY_PAID (219), aligning with the documented JSON-RPC behavior. tests/test_xpay.py is updated to assert the exact error code 219. No protocol, cryptographic, or authorization logic is changed.
Changed components
plugins/xpay/xpay.ctests/test_xpay.pyInspect captured patch +4 / −2
diff --git a/plugins/xpay/xpay.c b/plugins/xpay/xpay.c
index 24c688cd..4a96b42d 100644
--- a/plugins/xpay/xpay.c
+++ b/plugins/xpay/xpay.c
@@ -792,7 +792,7 @@ static void update_knowledge_from_error(struct command *aux_cmd,
if (ecode == PAY_INJECTPAYMENTONION_ALREADY_PAID) {
payment_give_up(aux_cmd, attempt->payment,
- PAY_INJECTPAYMENTONION_FAILED,
+ PAY_INJECTPAYMENTONION_ALREADY_PAID,
"Already paid this invoice successfully");
return;
}
diff --git a/tests/test_xpay.py b/tests/test_xpay.py
index e136dcf6..33fef669 100644
--- a/tests/test_xpay.py
+++ b/tests/test_xpay.py
@@ -156,10 +156,12 @@ def test_xpay_simple(node_factory):
assert ret['amount_msat'] == 10000
assert ret['amount_sent_msat'] == 10000
+ PAY_INJECTPAYMENTONION_ALREADY_PAID = 219
# Fails if we try to pay again
b11_paid = b11
- with pytest.raises(RpcError, match="Already paid"):
+ with pytest.raises(RpcError, match="Already paid") as err:
l1.rpc.xpay(b11_paid)
+ assert err.value.error['code'] == PAY_INJECTPAYMENTONION_ALREADY_PAID
# BOLT-11, indirect peer
b11 = l3.rpc.invoice('10000msat', 'test_xpay_simple', 'test_xpay_simple bolt11')['bolt11']
Why this scored 22/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.