Repository navigation
Description
Updating to 2.4.x permanently locks the LND wallet on deployments whose walletunlock.json still holds the legacy default password. The new password is lost and the wallet cannot be unlocked again.
Reproduced twice on the same instance, eight hours apart, with a Proxmox snapshot restore in between — so this is deterministic, not a one-off.
Trigger condition
walletunlock.json contains "wallet_password":"hellorockstar", so MIGRATE_DEFAULT=1 in docker-initunlocklnd.sh.
Mechanism
lnd_unlock_entrypoint deletes every macaroon including macaroons.db, driven by LND_MACAROON_ROTATION_ID. initunlocklnd then runs the default-password migration against a wallet whose macaroon root key no longer exists.
The first POST /v1/changepassword opens the wallet — so the current password is valid — changes the passphrase, then fails with default root key not found, because the root key was deleted seconds earlier. The script treats this as a failed migration, exits 1, and never writes NEWPASS to walletunlock.json.
The second call, using WALLETPASS_BASE64_CURRENT, fails with invalid passphrase for master public key — consistent with the passphrase having already been changed by the first call. Unlocking manually with the value from the file, via lncli unlock --stdin, fails the same way.
The wallet passphrase is now 32 bytes of urandom that exist nowhere. The unlock file still holds the old, now-invalid value.
Incorrect assumption in the code
From docker-initunlocklnd.sh:
# INSTEAD of a plain unlock, not after it. The file is only rewritten
# AFTER changepassword succeeds, so a failed migration never strands
# the wallet: next start just retries.That assumption does not hold. LND changes the wallet passphrase before touching the macaroon root key, so a failure on the second step leaves the passphrase already changed. And the rotation running immediately before guarantees that second step fails.
Impact
Wallet permanently locked. On-chain funds recoverable only from the seed; channels recoverable only via SCB with force closes. In our case a single channel had to be force-closed and the node rebuilt.
Possible fixes
Run the migration before the macaroon rotation; or persist NEWPASS to the unlock file before the call rather than after; or do not treat a root-key failure as a migration failure, since the passphrase change has already succeeded at that point.
Note for other operators
This is not a reason to skip the update — the vulnerability it fixes is far more serious. The condition is narrow, and taking a volume backup of lnd_bitcoin_datadir beforehand avoids any loss.
First occurrence — 07:30 UTC, LND 0.21.0-beta to 0.21.3-beta
externalip=<redacted>.onion:9735 added to /data/lnd.conf
[lnd_unlock_entrypoint] Rotating macaroons (lnd-security-2026-09-v2), ALL existing macaroons are being invalidated
/data/admin.macaroon
/data/data/chain/bitcoin/mainnet/macaroons.db
/data/data/chain/bitcoin/mainnet/walletkit.macaroon
/data/data/chain/bitcoin/mainnet/invoices.macaroon
/data/data/chain/bitcoin/mainnet/router.macaroon
/data/data/chain/bitcoin/mainnet/signer.macaroon
/data/data/chain/bitcoin/mainnet/chainnotifier.macaroon
/data/readonly.macaroon
/data/invoice.macaroon
[lnd_unlock_entrypoint] Macaroons removed, lnd will regenerate them. Every client must be re-paired
[initunlocklnd] Waiting 2 seconds for lnd...
2026-09-13 07:30:34.704 [INF] LTND: Version Info rev=391889 version=0.21.3-beta commit=basedon-v0.21.3-beta-1-fresh-btcpay debuglevel=production logging=info
2026-09-13 07:30:34.721 [INF] LTND: Opening the main database, this might take a few minutes...
2026-09-13 07:30:40.527 [INF] LTND: Creating local graph and channel state DB instances
2026-09-13 07:30:40.532 [WRN] CHDB: DB version key missing, recovering from db_version=33
2026-09-13 07:30:40.532 [INF] CHDB: Checking for schema update: latest_version=35, db_version=33
2026-09-13 07:30:40.532 [INF] CHDB: Applying migration #35
2026-09-13 07:30:40.549 [INF] LTND: Database(s) now open (time_to_open=5.828767263s)!
2026-09-13 07:30:40.551 [INF] LTND: Waiting for wallet encryption password.
2026-09-13 07:30:42.392 [ERR] RPCS: [/lnrpc.Lightning/GetInfo]: wallet locked, unlock it to enable full RPC access
[initunlocklnd] Wallet and Unlock files are present... parsing wallet password and unlocking lnd
2026-09-13 07:30:42.963 [INF] BTWL: Opened wallet
2026-09-13 07:30:45.156 [ERR] RPCS: [/lnrpc.WalletUnlocker/ChangePassword]: default root key not found
2026-09-13 07:30:45.755 [ERR] RPCS: [/lnrpc.WalletUnlocker/ChangePassword]: invalid passphrase for master public key
[initunlocklnd] WARNING: migration off the default password failed, lnd returned: {"code":2, "message":"invalid passphrase for master public key", "details":[]}
[initunlocklnd] Wallet is still locked, migration will be retried on next start
2026-09-13 07:30:46.135 [ERR] RPCS: [/lnrpc.Lightning/WalletBalance]: wallet locked, unlock it to enable full RPC access
Second occurrence — 17:16 UTC, after restoring a pre-update snapshot
alias=<redacted> added to /data/lnd.conf
LND_CHAIN=btc
LND_ENVIRONMENT=mainnet
Added bitcoin.active and bitcoin.mainnet to config file /data/lnd.conf
Waiting /root/.nbxplorer/btc_fully_synched to be created...
The chain is fully synched
externalip=<redacted>.onion:9735 added to /data/lnd.conf
[lnd_unlock_entrypoint] Rotating macaroons (lnd-security-2026-09-v2), ALL existing macaroons are being invalidated
/data/admin.macaroon
/data/data/chain/bitcoin/mainnet/macaroons.db
/data/data/chain/bitcoin/mainnet/walletkit.macaroon
/data/data/chain/bitcoin/mainnet/invoices.macaroon
/data/data/chain/bitcoin/mainnet/router.macaroon
/data/data/chain/bitcoin/mainnet/signer.macaroon
/data/data/chain/bitcoin/mainnet/chainnotifier.macaroon
/data/readonly.macaroon
/data/invoice.macaroon
[lnd_unlock_entrypoint] Macaroons removed, lnd will regenerate them. Every client must be re-paired
[initunlocklnd] Waiting 2 seconds for lnd...
2026-09-13 17:16:48.655 [INF] LTND: Version Info rev=391889 version=0.21.3-beta commit=basedon-v0.21.3-beta-1-fresh-btcpay debuglevel=production logging=info
2026-09-13 17:16:48.667 [INF] LTND: Opening the main database, this might take a few minutes...
2026-09-13 17:16:52.819 [INF] LTND: Creating local graph and channel state DB instances
2026-09-13 17:16:52.823 [WRN] CHDB: DB version key missing, recovering from db_version=33
2026-09-13 17:16:52.823 [INF] CHDB: Checking for schema update: latest_version=35, db_version=33
2026-09-13 17:16:52.823 [INF] CHDB: Applying migration #35
2026-09-13 17:16:52.843 [INF] LTND: Database(s) now open (time_to_open=4.175582196s)!
2026-09-13 17:16:52.845 [INF] LTND: Waiting for wallet encryption password.
2026-09-13 17:16:54.284 [ERR] RPCS: [/lnrpc.Lightning/GetInfo]: wallet locked, unlock it to enable full RPC access
[initunlocklnd] Wallet and Unlock files are present... parsing wallet password and unlocking lnd
2026-09-13 17:16:54.829 [INF] BTWL: Opened wallet
2026-09-13 17:16:56.862 [ERR] RPCS: [/lnrpc.WalletUnlocker/ChangePassword]: default root key not found
2026-09-13 17:16:57.364 [ERR] RPCS: [/lnrpc.WalletUnlocker/ChangePassword]: invalid passphrase for master public key
[initunlocklnd] WARNING: migration off the default password failed, lnd returned: {"code":2, "message":"invalid passphrase for master public key", "details":[]}
[initunlocklnd] Wallet is still locked, migration will be retried on next start
Successful unlock on 0.21.0-beta the day before, for comparison
2026-09-12 15:17:41.745 [INF] LTND: Version Info rev=ee13d2 version=0.21.0-beta commit=basedon-v0.21.0-beta-fresh-btcpay
2026-09-12 15:18:00.783 [INF] LTND: Waiting for wallet encryption password.
2026-09-12 15:18:01.834 [INF] BTWL: Opened wallet
2026-09-12 15:18:02.424 [INF] BTWL: The wallet has been unlocked without a time limit
cc @rockstardev
Activity
[-]Macaroon rotation breaks the hellorockstar password migration, permanently locking the LND wallet[/-][+]Macaroon rotation breaks the previous default password migration, permanently locking the LND wallet[/+]NicolasDorier commentedon Sep 14, 2026
Removed macaroon rotation... we will need to communicate on it it is a critical failure from us.
615664b
NicolasDorier commentedon Sep 14, 2026
@AirHache thank you for this report. We will work on an announcement as well as the recovery procedure.
Luckily, given the channel backup and seed funds aren't lost.
Everybody who:
Got impacted by this.
If they updated after 2026-09-04T01:26:39.000Z (779274a) but before 2026-09-11T02:57:48.000Z (b4e8ce6), then there is no problem as the password got changed before the macaroon was rotated.
NicolasDorier commentedon Sep 14, 2026
@AirHache also, can you DM me on X ?
AirHache commentedon Sep 14, 2026
Hi,
DMed you this morning.
rockstardev commentedon Sep 16, 2026
We have a release candidate to fix this:
btcpayserver/lnd#17 (comment)
We'll likely need to yank current LND_MACAROON_ROTATION_ID code.
damanic commentedon Oct 10, 2026
I just updated to 2.4.5 and have been hit with locked LND wallet - macaroon deleted!?
On further investigation it looks as though the macaroon assault occurred before the 2.4.5 update and I did not notice.
@NicolasDorier
Can you point me to the recovery procedure please.
NicolasDorier commentedon Oct 10, 2026
@damanic it seems to be the problem yes. @rockstardev is there a recovery procedure? The user has the seed and the channel backups, so technically they should be able to recover the funds.
AirHache commentedon Oct 10, 2026
Hi,
Standard lnd recovery guide is here: https://docs.lightning.engineering/lightning-network-tools/lnd/disaster-recovery
I personally chose to restore the VM since I had backup and no activity on my btcpayserver.
damanic commentedon Oct 10, 2026
That guide says I need to set up a new node - really?
Before I jump into it I would like to get the official BTCPAY dev advice considering it is a known issue with a BTCPAY version release that caused this 'disaster'.
AirHache commentedon Oct 10, 2026
Sure, they may propose a smoother approach.
Anyway, if you have seed and channel.backup, you should be fine
damanic commentedon Oct 11, 2026
OK so my node is back up and running, remarkably no channels force closed.
Here is how I resolved it on my docker deployment.
All commands are presented for example only, do not copy and paste blindly. Check where your data resides, container names etc and adjust accordingly. THIS WORKED FOR ME, VERIFY IT FOR YOUR SELF.
Get the seed; BTCPay keeps it in walletunlock.json , and you can get it from BTCPAY backend
Server Settings-> Services -> LND Seed BackupCreate a new wallet password and save it before using it
NicolasDorier commentedon Oct 11, 2026
@damanic thank you for documenting this. I am sorry about this bug which could really have made people lose their money... we got lucky @AirHache catched it fast enough so not many people fell in this issue. We will communicate on that in case some people like you didn't see that their nodes were offline.
damanic commentedon Oct 11, 2026
@NicolasDorier
How does BTCPAY communicate issues like this outside of github issues?
I think this comment should have moved quickly to a public announcement post with steps to resolve.
Perhaps there should also be an option in BTCPAY for users to enable 'blog' notifications for 'announcements' or other categories. Once enabled they are revealed through the 'bell' icon in the same way as new release notifications.
rockstardev commentedon Oct 12, 2026
Thanks @damanic for writing this up, it is the right approach. I reproduced the locked wallet on regtest with the LND image BTCPay Server ships and tested your recovery end to end: the channel came back with its balance, the on-chain funds were recovered, and payments worked both ways.
One addition for anyone following it: move the old macaroon files aside too (
/data/*.macaroonand/data/data/chain/bitcoin/mainnet/*.macaroon), together withwallet.dbandmacaroons.db. LND only writes new macaroon files when none exist, and the old ones fail against the rebuilt wallet withverification failed: signature mismatch, so BTCPay Server cannot connect to LND. Your node may not have needed this if those files were already gone.We are adding a step-by-step guide to the docs, based on your steps: btcpayserver/btcpayserver-doc#1649