Tags: @TheBlueMatt @leonash
The chanmon_consistency fuzz target started failing after 369a2cf9c8ef810deea0cd2b4cf6ed0691b78144 on April 2, 2026, which merged PR #4529, "Add unannounced_channel_max_inbound_htlc_value_in_flight_percentage".
Repro
This input reproduces the failure on 369a2cf9c8ef810deea0cd2b4cf6ed0691b78144:
8584328489ffffff3a3a3a3a3a3a3affffffff3a3a3a3a3a3a3a3a3a3a
On 369a2cf9c8ef810deea0cd2b4cf6ed0691b78144, this panics at:
lightning/src/ln/channel.rs:6236
assertion failed: remote_stats.commitment_stats.holder_balance_msat >=
funding.counterparty_selected_channel_reserve_satoshis.unwrap_or(0) * 1000
The backtrace goes through:
ChannelContext::get_available_balances_for_scope
FundedChannel::get_available_balances
ChannelDetails::from_channel
ChannelManager::list_funded_channels_with_filter
chanmon_consistency::send_payment
Regression window
I compared:
369a2cf9c8ef810deea0cd2b4cf6ed0691b78144
- first parent,
77a0e4545
The same input:
- panics on
369a2cf9c8ef810deea0cd2b4cf6ed0691b78144
- exits cleanly on
77a0e4545
So this is a real regression introduced by the merge.
What changed
The failing assertion itself predates the merge. What changed is the state space that can now reach it.
PR #4529 split the inbound in-flight percentage setting into announced and unannounced variants, and also changed defaults to:
- announced channels:
25%
- unannounced channels:
100%
Before this change, the effective default was 10%.
In the repro logs, the affected announced channels are opened with:
- pre-merge:
max_htlc_value_in_flight_msat: 10000000
- post-merge:
max_htlc_value_in_flight_msat: 25000000
That higher limit appears to let get_available_balances advertise more outbound capacity than can actually be added while preserving the reserve invariant on the next remote commitment.
Observed bad path
The problematic channel in the repro is ch:8c0000.
On 369a2cf9c8ef810deea0cd2b4cf6ed0691b78144:
- a
10_000_000 msat outbound HTLC is sent on the channel
- repeated
1_000_000 msat sends are then accepted into the holding cell while awaiting RAA / monitor completion
- later,
list_funded_channels_with_filter asks for channel details
get_available_balances_for_scope simulates one more outbound HTLC using balances.next_outbound_htlc_limit_msat
- that simulation drops
remote_stats.commitment_stats.holder_balance_msat below counterparty_selected_channel_reserve_satoshis, tripping the debug assertion
On the pre-merge parent, the same later sends are rejected with:
Cannot send more than our next-HTLC maximum - 0 msat
That strongly suggests we are now overestimating next_outbound_htlc_limit_msat for this state.
Likely root cause
This looks like an available-balance computation bug, not a fuzz harness issue.
Relevant code paths:
lightning/src/sign/tx_builder.rs, get_available_balances
lightning/src/ln/channel.rs, get_available_balances_for_scope
The debug check in get_available_balances_for_scope is effectively saying that the advertised "max next outbound HTLC" is not actually safe, because adding an HTLC of exactly that size violates the reserve requirement on the next remote commitment.
Security status
Current assessment:
- This is a confirmed correctness regression.
- This is a reachable panic in fuzzing / debug-assertion builds.
- This is not currently a confirmed fund-loss bug.
- This is not currently a confirmed release-build panic, because the failing assertion is debug-only.
- This is security-adjacent, because it means channel liquidity can be overstated and HTLC admission can be inconsistent with reserve constraints.
What I have not yet proven is whether release builds can drive this same state into:
- a clean payment failure only
- a channel error / forced close
- a broader remote-triggerable DoS condition
So I would treat it as:
- not a confirmed security vulnerability at this point
- worth fixing promptly
- worth checking in release mode to determine whether the impact is limited to misreported liquidity and payment failure, or can escalate into channel disruption
Suggested follow-up
get_available_balances should likely be tightened so that next_outbound_htlc_limit_msat never advertises a value that would cause the next remote commitment to violate:
holder_balance_msat >= counterparty_selected_channel_reserve_satoshis * 1000
In other words, the debug assertion looks valid, but the computed outbound limit is too permissive in this state after the PR changed the HTLC in-flight defaults.
[AI]
Tags: @TheBlueMatt @leonash
The
chanmon_consistencyfuzz target started failing after369a2cf9c8ef810deea0cd2b4cf6ed0691b78144on April 2, 2026, which merged PR#4529, "Addunannounced_channel_max_inbound_htlc_value_in_flight_percentage".Repro
This input reproduces the failure on
369a2cf9c8ef810deea0cd2b4cf6ed0691b78144:On
369a2cf9c8ef810deea0cd2b4cf6ed0691b78144, this panics at:The backtrace goes through:
ChannelContext::get_available_balances_for_scopeFundedChannel::get_available_balancesChannelDetails::from_channelChannelManager::list_funded_channels_with_filterchanmon_consistency::send_paymentRegression window
I compared:
369a2cf9c8ef810deea0cd2b4cf6ed0691b7814477a0e4545The same input:
369a2cf9c8ef810deea0cd2b4cf6ed0691b7814477a0e4545So this is a real regression introduced by the merge.
What changed
The failing assertion itself predates the merge. What changed is the state space that can now reach it.
PR
#4529split the inbound in-flight percentage setting into announced and unannounced variants, and also changed defaults to:25%100%Before this change, the effective default was
10%.In the repro logs, the affected announced channels are opened with:
max_htlc_value_in_flight_msat: 10000000max_htlc_value_in_flight_msat: 25000000That higher limit appears to let
get_available_balancesadvertise more outbound capacity than can actually be added while preserving the reserve invariant on the next remote commitment.Observed bad path
The problematic channel in the repro is
ch:8c0000.On
369a2cf9c8ef810deea0cd2b4cf6ed0691b78144:10_000_000 msatoutbound HTLC is sent on the channel1_000_000 msatsends are then accepted into the holding cell while awaiting RAA / monitor completionlist_funded_channels_with_filterasks for channel detailsget_available_balances_for_scopesimulates one more outbound HTLC usingbalances.next_outbound_htlc_limit_msatremote_stats.commitment_stats.holder_balance_msatbelowcounterparty_selected_channel_reserve_satoshis, tripping the debug assertionOn the pre-merge parent, the same later sends are rejected with:
That strongly suggests we are now overestimating
next_outbound_htlc_limit_msatfor this state.Likely root cause
This looks like an available-balance computation bug, not a fuzz harness issue.
Relevant code paths:
lightning/src/sign/tx_builder.rs,get_available_balanceslightning/src/ln/channel.rs,get_available_balances_for_scopeThe debug check in
get_available_balances_for_scopeis effectively saying that the advertised "max next outbound HTLC" is not actually safe, because adding an HTLC of exactly that size violates the reserve requirement on the next remote commitment.Security status
Current assessment:
What I have not yet proven is whether release builds can drive this same state into:
So I would treat it as:
Suggested follow-up
get_available_balancesshould likely be tightened so thatnext_outbound_htlc_limit_msatnever advertises a value that would cause the next remote commitment to violate:In other words, the debug assertion looks valid, but the computed outbound limit is too permissive in this state after the PR changed the HTLC in-flight defaults.
[AI]