Repository navigation
Open
Description
After a restart of the node, it automatically force-closed a channel but an UTXO in the closing transaction (resulting from a pending HTLC) is missing in the output of listfunds.
CLN version: v25.12.1
Bitcoind version: 31.0
funding transaction: 7e907aa45a014c1666a5d1eefe98d4245b616abd02b9398d42293bc23aca2cce
closing transaction: 5bf58ab7e136d74d837472eaa907a5b3c508e4dfd8a20063c4814ebdddc1de4b
The closing transaction results in 5 outputs:
- anchor
- anchor
- the missing unspent UTXO (215289 sats)
- remaining funds of their side (1860779 sats)
- remaining funds of our side (9922922 sats)
listclosedchannels shows we should own this UTXO:
"final_to_us_msat": 10138211724
bkpr-listaccountevents shows this output as onchain_fee :
{
"account": "ce2cca3ac23b29428d39b902bd6a615b24d498feeed1a566164c015aa47a907f",
"type": "onchain_fee",
"tag": "onchain_fee",
"credit_msat": 215289724,
"debit_msat": 0,
"currency": "bc",
"timestamp": 1780321071,
"txid": "5bf58ab7e136d74d837472eaa907a5b3c508e4dfd8a20063c4814ebdddc1de4b"
}
The logs of this channel just after I restarted CLN:
2026-06-01T12:30:39.470Z UNUSUAL 03a93b87bf9f052b8e862d51ebbac4ce5e97b5f4137563cd5128548d7f5978dda9-chan#11240: Peer permanent failure in CHANNELD_NORMAL: channeld: sent Awaiting unilateral close (reason=local)
2026-06-01T12:30:39.470Z INFO 03a93b87bf9f052b8e862d51ebbac4ce5e97b5f4137563cd5128548d7f5978dda9-chan#11240: State changed from CHANNELD_NORMAL to AWAITING_UNILATERAL
2026-06-01T12:30:39.472Z **BROKEN** 03a93b87bf9f052b8e862d51ebbac4ce5e97b5f4137563cd5128548d7f5978dda9-chan#11240: Cannot broadcast our commitment tx: they have a future one
2026-06-01T13:37:51.100Z UNUSUAL 03a93b87bf9f052b8e862d51ebbac4ce5e97b5f4137563cd5128548d7f5978dda9-chan#11240: Peer permanent failure in AWAITING_UNILATERAL: Funding transaction spent: onchain txid 5bf58ab7e136d74d837472eaa907a5b3c508e4dfd8a20063c4814ebdddc1de4b (reason=unknown)
2026-06-01T13:37:51.100Z **BROKEN** 03a93b87bf9f052b8e862d51ebbac4ce5e97b5f4137563cd5128548d7f5978dda9-chan#11240: Cannot broadcast our commitment tx: they have a future one
2026-06-01T13:37:51.100Z INFO 03a93b87bf9f052b8e862d51ebbac4ce5e97b5f4137563cd5128548d7f5978dda9-chan#11240: State changed from AWAITING_UNILATERAL to FUNDING_SPEND_SEEN
2026-06-01T13:37:51.129Z **BROKEN** 03a93b87bf9f052b8e862d51ebbac4ce5e97b5f4137563cd5128548d7f5978dda9-onchaind-chan#11240: ERROR: Unknown commitment #1457929, recovering our funds!
2026-06-01T13:37:51.129Z INFO 03a93b87bf9f052b8e862d51ebbac4ce5e97b5f4137563cd5128548d7f5978dda9-chan#11240: State changed from FUNDING_SPEND_SEEN to ONCHAIN
2026-06-02T07:38:44.777Z INFO 03a93b87bf9f052b8e862d51ebbac4ce5e97b5f4137563cd5128548d7f5978dda9-chan#11240: onchaind complete, forgetting peer
What I've tried:
dev-rescan-outputs--rescanwith a large value (before the funding transaction)
Metadata
Metadata
Assignees
Labels
No labels
Activity
nsabedra27-glitch commentedon Jun 10, 2026
It wouldn't ever show my current bitcoin address
sangbida commentedon Nov 5, 2026
Thanks for such a detailed report!
Your logs show the peer had a newer commitment than your node (they have a future one), so your node waited for them to close, and they closed with a commitment yours had never seen (Unknown commitment #1457929). In that case we can only recover the
to_remoteoutput (your 9922922 sat). The 215289 sat is a pending HTLC output; spending it needs the peer's per-commitment point for that commitment, which we didn't keep.The
onchain_feeentry is the bookkeeper balancing the account, not a real fee: that output is still unspent at 5bf58ab7…de4b:2, the HTLC has long expired, and the peer never claimed it, so it's yours. Rescans can't find it because it was never a wallet address.Are you able to reach out to the operator of peer node for the per-commitment point of commitment #1457929 and the payment_hash and cltv_expiry of the 215289 sat HTLC. Unfortunately, there isn't an inbuilt tool at the moment to recover the funds yet but we could write a script for you to try recover these funds.
This is quite strange for this to happen on a simple restart, did you have to restore from a backup?
micoq commentedon Nov 7, 2026
Thank you for your response and the explanations!
There was no data recovery (and I know that's a very bad idea!), just a reboot of the node.
The shutdown was clean, and there was no data corruption at the file system (or block) level. The node directory (with its SQLite database) is on a RAID 1 array that was intact. I rechecked the entire array after noticing the problem and there was no corruption.
I will try to contact the node owner.
Edit: Thanks to the peer node owner, here are the required values:
These values seem correct: I was able to reconstruct the witness script and its hash is equal to the
ScriptPubKeyof the UTXO.Now, I just need a little help so I can build the final transaction and sign it correctly.
sangbida commentedon Nov 12, 2026
Hey @micoq! It’s great that you were able to recover all of the information and verify that the witness script matches the stranded output.
I’ve added a
recoverhtlccommand tolightning-hsmtoolso you can build and sign the recovery transaction, and so other people can use it if they end up in the same situation.The PR is here:
#9730
The patch can be downloaded directly from:
https://github.com/ElementsProject/lightning/pull/9730.patch
The branch is here:
https://github.com/sangbida/lightning/tree/hsmtool/recover-htlc
Please note that this PR has not been reviewed or merged yet and the command will sign a transaction spending real funds.
You do not need to replace or upgrade your running
lightningd.lightning-hsmtoolis standalone and does not connect to your node.If you installed CLN from a binary package and do not already have a source tree, you can build just the tool separately:
git clone --recurse-submodules \ --branch hsmtool/recover-htlc \ https://github.com/sangbida/lightning.git lightning-recoverhtlc cd lightning-recoverhtlc ./configure make -j"$(nproc)" tools/lightning-hsmtoolThis produces
tools/lightning-hsmtool. You do not need to install the build or run thelightningdit creates.I would use copies of your database and
hsm_secretwhile doing this. Please never send yourhsm_secretto me or anyone else.You still need the channel DBID and the peer’s HTLC basepoint. They should be in the surviving channel row. You can read them from a copy of the database with:
Please check the ID rather than assuming
11240from the log is correct.Then create a
recovery.jsonfile like this:{ "version": 1, "network": "bitcoin", "peer_id": "03a93b87bf9f052b8e862d51ebbac4ce5e97b5f4137563cd5128548d7f5978dda9", "channel_dbid": <channel DBID>, "remote_htlc_basepoint": "<htlc_basepoint_remote>", "commitment_point": "0381c3d143f665a949eecc8b02fceffa7774a33af80dbc5a3b484fa5fa6f6e3d67", "commitment_tx": "<raw hex of 5bf58ab7e136d74d837472eaa907a5b3c508e4dfd8a20063c4814ebdddc1de4b>", "feerate_perkw": 2500, "htlcs": [ { "direction": "out", "payment_hash": "e0258417c9514babeb6d9d5f3db5db9fec81755d81da93eb3690e24407c430ec", "cltv_expiry": 952295 } ] }Run it with a fresh Bitcoin address that you control:
./tools/lightning-hsmtool recoverhtlc \ recovery.json \ <destination-address> \ /path/to/copied-hsm_secretThe tool checks that the reconstructed HTLC script actually matches the commitment output before signing. It does not broadcast anything or print private keys.
It will print the signed transaction and commands for checking it with
decoderawtransactionandtestmempoolaccept. Please inspect those results before broadcasting it withsendrawtransaction.The alternative is to wait until this has been reviewed, merged and included in a release, but I don’t want you to have to leave the funds stranded while waiting. If you would prefer, send me the DBID, remote HTLC basepoint and raw commitment transaction—none of those are secret—and I can help check the bundle before you run it.
Before running anything, please independently verify every value in the recovery bundle against your own database, logs and the on-chain transaction. In particular, please verify:
htlc_basepoint_remote"direction": "out")Please do not rely only on the values or interpretation in this comment. Read through the PR and the tool output yourself, and inspect the signed transaction with
decoderawtransactionandtestmempoolacceptbefore broadcasting it. If anything does not match what you expect, stop rather than broadcasting.