Skip to content

fix(nwc): decode bolt11 before NWC call to prevent payment record loss - #4037

Open
dadofsambonzuki wants to merge 2 commits into
lnbits:devfrom
dadofsambonzuki:fix/nwc-pay-invoice-checking-id
Open

fix(nwc): decode bolt11 before NWC call to prevent payment record loss#4037
dadofsambonzuki wants to merge 2 commits into
lnbits:devfrom
dadofsambonzuki:fix/nwc-pay-invoice-checking-id

Conversation

@dadofsambonzuki

@dadofsambonzuki dadofsambonzuki commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Some NWC transactions were being silently dropped from the DB despite being paid as the transaction is being rolled back at the database level.

The flow:

  1. _pay_external_invoice calls create_payment (INSERT) inside a db.connect() transaction
  2. NWC pay_invoice sends the request to the rizful relay — the provider processes it
  3. But the relay response is lost/delayed, so self.conn.call("pay_invoice", ...) raises an exception
  4. The outer except Exception in nwc.py:637 returns PaymentResponse(error_message=msg) — checking_id is None because payment_hash was never extracted
  5. _pay_external_invoice sees checking_id is None, marks the payment FAILED, then raises PaymentError
  6. The exception propagates out of db.connect() → SQLAlchemy rolls back the entire transaction — the create_payment INSERT is undone
  7. The payment record vanishes from the database entirely
  8. NWC provider already sent the sats, but LNbits has zero record

@dadofsambonzuki

Copy link
Copy Markdown
Contributor Author

@blackcoffeexbt

@motorina0
motorina0 requested a review from dni July 13, 2026 12:07
@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 64.29%. Comparing base (8c7e5c2) to head (24d04a5).
⚠️ Report is 178 commits behind head on dev.

Files with missing lines Patch % Lines
lnbits/wallets/nwc.py 50.00% 6 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #4037      +/-   ##
==========================================
+ Coverage   56.50%   64.29%   +7.78%     
==========================================
  Files         123      129       +6     
  Lines       16914    18979    +2065     
==========================================
+ Hits         9557    12202    +2645     
+ Misses       7357     6777     -580     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@motorina0
motorina0 force-pushed the fix/nwc-pay-invoice-checking-id branch from ef8d0a9 to 9bcfe50 Compare July 13, 2026 12:13
Comment thread lnbits/wallets/nwc.py Outdated
return StatusResponse(str(e), 0)

async def pay_invoice(self, bolt11: str, fee_limit_msat: int) -> PaymentResponse:
invoice_data = bolt11_decode(bolt11)

@dni dni Jul 13, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you should put this in its own try / catch bolt11 decoding can throw an Bolt11Exception. return a failed paytment_repsonse then.

dadofsambonzuki and others added 2 commits July 13, 2026 15:39
When pay_invoice raises an exception before bolt11_decode runs,
payment_hash is never set, so PaymentResponse is returned with
checking_id=None. LNbits core then marks the payment as failed
and the database transaction is rolled back, losing the record
entirely even though the NWC provider processed the payment.

Move bolt11_decode before the try block so payment_hash is always
available. The outer except block now returns a pending response
with checking_id set, letting the core commit the payment record
and settle it later via get_payment_status.
@dni
dni force-pushed the fix/nwc-pay-invoice-checking-id branch from 307ad9b to 24d04a5 Compare July 13, 2026 13:39
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.

3 participants