Skip to content

xpay: stop treating completed commands as ongoing - #9717

Open
darioAnongba wants to merge 1 commit into
ElementsProject:masterfrom
darioAnongba:fix/xpay-final-failure-status
Open

darioAnongba wants to merge 1 commit into
ElementsProject:masterfrom
darioAnongba:fix/xpay-final-failure-status

Conversation

@darioAnongba

@darioAnongba darioAnongba commented Nov 7, 2026 •

Copy link
Copy Markdown

xpay can return its final failure while listpays still reports pending, even when all payment parts have failed.

payment_give_up() clears payment->cmd after replying, but the payment remains in xpay->payments until outstanding requests and private-layer cleanup finish. attempt_ongoing() currently counts that cleanup-only entry as an active payment. This can leave a caller that checks status after the final RPC error with a stale pending result.

Require payment->cmd in attempt_ongoing(), preserving the existing hash/group match. Unresolved HTLCs still produce pending through listpays' existing PAYMENT_PENDING check.

The regression pays a deleted invoice between two real nodes, waits for the final xpay error, and holds askrene-remove-layer through an asynchronous RPC hook. It checks listpays while cleanup is paused, then releases the hook in finally. Both normal and slow modes are covered, without a timing-dependent sleep.

Validation

  • Reproduced on released v26.06.9 and v26.06.10.
  • Built the C node and plugins from master 4fc822f94c805edff71572cb12eaea902c3ce3c0 with --disable-rust --disable-valgrind --enable-static.
  • Before the fix, both new regression cases fail with assert 'pending' == 'failed'.
  • After the fix, all six selected cases pass:
VALGRIND=0 RUST=0 python3 -m pytest \
  tests/test_xpay.py::test_listpays_after_xpay_failure \
  tests/test_xpay.py::test_attempt_notifications \
  tests/test_xpay.py::test_xpay_slow_mode \
  tests/test_xpay.py::test_fail_after_success -v --timeout=120
  • Python lint and git diff --check pass.

payment_give_up clears payment->cmd when it returns a final failure, but
keeps the payment in xpay->payments until asynchronous requests and private
layer removal finish. attempt_ongoing currently treats that cleanup-only
entry as active, so listpays can return pending after xpay has failed even
when all its parts have failed.

Require an active command when checking whether xpay is still trying the
payment. listpays continues to report pending when an HTLC is unresolved
through its existing PAYMENT_PENDING check.

Add a regression which holds askrene-remove-layer after the final xpay
error and checks listpays before releasing cleanup. Exercise both normal
and slow modes so the failure does not depend on scheduling the lookup
inside a short cleanup window.

Changelog-Fixed: listpays no longer reports failed xpay payments as pending while asynchronous cleanup runs.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant