ElementsProject / ElementsProject/lightning
`close` RPC deadlocked: recorded Taproot shutdown script conflicts with peer lacking `option_shutdown_anysegwit`, no way to override
- Dominant language
- C
- Stars
- 3.1k
- Forks
- 1k
- Avg merge
- 4d 10h
- Merged PRs (30d)
- 13
Description
## Title
`close` RPC deadlocked: recorded Taproot shutdown script conflicts with peer lacking `option_shutdown_anysegwit`, no way to override
## Summary
A channel stuck in `CHANNELD_SHUTTING_DOWN` cannot be closed — neither cooperatively
nor via the `unilateraltimeout` fallback — because the channel's already-committed
local shutdown script (`close_to_addr`) is a Taproot (v1 segwit, `bc1p...`) address,
and the peer does not support `option_shutdown_anysegwit`. Attempting to override the
destination on a subsequent `close` call is rejected because it doesn't match the
previously-sent shutdown script. This produces two mutually exclusive error paths with
no RPC-level way to reconcile them, leaving the channel permanently un-closeable
through normal means.
## Environment
- `lightningd` version: `v26.06.6`
- OS: `Ubuntu 24.04.4 LTS`
- Channel type: `static_remotekey/even`, `anchors/even`
## Steps to reproduce
1. Have an open channel (`CHANNELD_NORMAL`) with a peer that does **not** advertise
`option_shutdown_anysegwit` in its node features.
2. Node's default `close_to` resolves to a Taproot (`bc1p...`) address (e.g. via
wallet default address type), and a `close` is issued, sending a `shutdown`
message and transitioning the channel to `CHANNELD_SHUTTING_DOWN`.
3. Peer never completes the cooperative close (goes offline / unresponsive). Channel
remains in `CHANNELD_SHUTTING_DOWN` indefinitely.
4. Attempt to force a unilateral close:
```
lightning-cli close 1
```
Result:
```
{
"code": -32602,
"message": "Peer does not allow v1+ shutdown addresses"
}
```
5. Attempt to override the destination with a native SegWit (`bc1q...`) address to
work around the above:
```
lightning-cli newaddr bech32
lightning-cli close 1
```
Result:
```
{
"code": -32602,
"message": "Destination address does not match already-sent shutdown script "
}
```
## Expected behavior
Either:
- The `unilateraltimeout` fallback should be able to force a unilateral close
(broadcasting the latest commitment transaction) without depending on constructing
a valid *cooperative* shutdown script at all, since a unilateral close does not use
`close_to` for the immediate output — it settles to the standard to-local delayed
script and is later swept by `onchaind`. The v1+/anysegwit check should not block
this fallback path.
- Or, at minimum, there should be an RPC-level way to reset/override the recorded
shutdown script for a channel that has not yet completed cooperative close, so a
compatible (v0) destination can be substituted and negotiation can proceed.
## Actual behavior
The channel is left in a permanent deadlock:
- The `close` RPC requires the destination to match the already-sent (Taproot)
shutdown script.
- The already-sent shutdown script is rejected by the peer's feature set.
- No parameter combination of `close` resolves this. There does not appear to be a
non-destructive way to reset the local shutdown script or force a unilateral close
that bypasses cooperative-script validation.
## Impact
Funds remain safely locked in the channel's on-chain funding output (not lost), but
are inaccessible via any documented RPC path. The only known escapes
(`dev-forget-channel`, manual `hsmtool`-based commitment reconstruction) are
destructive/advanced and not appropriate as a routine remedy for what looks like a
straightforward feature-negotiation edge case.
## Relevant output (redacted)
```
listpeerchannels:
{
"peer_id": "",
"peer_connected": false,
"channel_type": {
"bits": [12, 22],
"names": ["static_remotekey/even", "anchors/even"]
},
"state": "CHANNELD_SHUTTING_DOWN",
"scratch_txid": "",
"channel_id": "",
"funding_txid": "",
"funding_outnum": 1,
"close_to_addr": "",
"close_to": "",
"opener": "remote",
"closer": "local",
"to_us_msat": 9889360982,
"total_msat": 10000000000,
"our_to_self_delay": 1201,
"their_to_self_delay": 144,
"state_changes": [
{
"timestamp": "2026-01-14T21:16:29.308Z",
"old_state": "CHANNELD_AWAITING_LOCKIN",
"new_state": "CHANNELD_NORMAL",
"cause": "remote",
"message": "Lockin complete"
},
{
"timestamp": "2026-07-23T13:47:56.871Z",
"old_state": "CHANNELD_NORMAL",
"new_state": "CHANNELD_SHUTTING_DOWN",
"cause": "user",
"message": "User or plugin invoked close command"
}
],
"status": ["Loaded from database"],
"htlcs": []
}
```
Peer's advertised features (from `listnodes`) do not include
`option_shutdown_anysegwit` (bit 26/27) — happy to paste the raw feature bitfield if
useful, omitted here as it may help fingerprint the peer.
## Additional notes
- The peer has been offline (`peer_connected: false`) for the entire duration, so
cooperative resolution isn't possible regardless — the request here is specifically
about the unilateral fallback path being blocked by the same script-compatibility
check that only matters for the cooperative path.
- Happy to provide additional redacted output or test against a patch if useful.
- Full Disclosure - used AI to assist in generating content.
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the `close` RPC handling for channels in `CHANNELD_SHUTTING_DOWN`, then trace the `unilateraltimeout` fallback and the `onchaind` path. Reproduce the conflict with a Taproot `close_to` address and a peer lacking `option_shutdown_anysegwit`; done means unilateral fallback is no longer blocked by cooperative shutdown-script validation, or the recorded destination can be safely overridden.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- c
- Domain
- networking, payments
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100