← Tech docs

How to clear a structural flag.

June 2026

When a depleted channel keeps failing to refill at a price it can ever earn back, the node stops grinding it and raises a structural flag — a "this is a capital decision, not a pricing problem" signal. A common question follows: once flagged, does it ever clear, or is it stuck forever? It clears — but the path is narrower than you'd expect, and one obvious recovery case is deliberately ignored. This note explains both.

What is the structural flag, exactly?

It is a live verdict, recomputed every run, not a sticky state someone has to reset. Two conditions must both hold:

structural = profit_capped AND failures_since_last_success ≥ THRESHOLD

profit_capped means the channel is calibrated (enough trailing outbound volume to trust its earnings) and the price it would take to refill it has escalated past what it earns back — earned_ppm × PROFIT_HORIZON. THRESHOLD is REBALANCE_STRUCTURAL_FAIL_THRESHOLD (5) consecutive failed refill attempts. When both are true the planner drops the channel as a rebalance target, a one-time structural_liquidity alert fires, and structural_flag_ts is stamped.

When does the timestamp get stamped?

By both the 2h fee loop and the nightly recompute, through one shared helper (_structural_flag_ts): first-stamp on entry, kept while it persists, cleared to 0 on recovery. Whichever job runs first stamps it, so the flag trips within one 2h cycle rather than waiting up to a day for the nightly pass. The alert is gated on the prior stamp, so it still fires exactly once.

Does it have to fail 5 real attempts?

Not necessarily — a failed cycle can now also be a QueryRoutes early-out. Before spending an attempt, the planner probes the cheapest chunk for a live route within the channel's affordable ceiling (a dry-run, no payment). If there's none, refilling is a capital problem, not price discovery, so it skips the wasted attempt and records a synthetic failed cycle (failure_reason='QR_NO_AFFORDABLE_ROUTE') that counts toward the threshold exactly like a real refusal. The point: the early-out replaces the wasted attempts, not the stranding they would eventually trigger — the channel still reaches its structural flag and surfaces the capital decision, just without burning cycles probing routes that can't settle. It's calibrated-only, and a probe that's unavailable (LND down) is never counted, so a transport blip can't strand a channel. See rebalancing.

So how does it clear?

The helper writes 0 on the next run where the verdict goes false — which means flipping either term back. There are five ways:

1. A successful refill   → failures_since_last_success resets to 0 → not structural
2. Earnings climb        → earned_ppm × PROFIT_HORIZON > escalated → not profit_capped
3. Channel goes UNJUDGED → too little volume over the FULL max lookback (90d) → cap evaporates
4. Liquidity recovers    → local_ratio ≥ REBALANCE_TARGET (≥50%) → structural cleared outright
5. Failures EXPIRE       → refusals older than the max lookback (90d) stop counting → fails < 5

Paths 1–3 are covered below; path 4 — the recovery escape — is covered under "Does refilling the channel clear it?"

Path 5 mirrors path 3: failure evidence ages out on the same 90-day clock as earnings evidence. There's a subtle honesty argument behind it — a profit-capped channel's failed attempts all bid the cap price, not the escalated levels the formula imagines, so carrying those refusals forever would inflate any future re-entry bid with prices that were never actually tested. With expiry, a channel returning to planning resumes at last_refill × 1.0 and rebuilds escalation from fresh attempts — and a flag nothing else clears earns a free 5-attempt re-probe roughly once a quarter: if the market still refuses, it re-flags within hours at zero cost; if routing prices moved, the channel quietly comes back to life.

Why doesn't it just clear on its own, then?

Because of a deliberate blind spot in path 1. A successful rebalance is what normally zeroes the failure counter — but the planner drops structural targets, so the pipeline never attempts a refill into a flagged channel, so it never produces the success that would clear it. The counter stays frozen at ≥ threshold. Left purely to the auto-loop on a steady sink, the flag persists — which is arguably correct, since it is a standing capital decision. To clear it via path 1 you have to act: an operator-forced rebalance (ln-operator rebalance --force …) that actually lands.

What about paths 2 and 3?

Path 2 (earnings climb past the cap) is real but rarely fires on a true sink — it needs the channel to start earning far more on outbound than the refill costs, which is exactly what wasn't happening. Path 3 is the quiet one: if a flagged channel goes idle long enough that its outbound volume drops below EARNED_PPM_MIN_VOLUME_SATS over the entire max lookback (EARNED_PPM_MAX_LOOKBACK_DAYS, 90 days), it falls back to calibrating — there's no longer enough evidence to cap it — so profit_capped goes false and the flag clears.

Path 3 used to be much sharper than it sounds. The calibration window was a flat 21 days, and a structural channel is quiet because it's depleted — so the window reliably drained, the cap evaporated after three quiet weeks, and the budget that came back was the full failure-escalation (last_refill × (1 + 0.2 × failures), clipped at REBALANCE_MAX_BUDGET_PPM): a channel the gate had ruled unprofitable at 1,400 ppm would silently re-enter planning bidding 5,000. The fix: the earned-ppm window now widens when the standard 21 days are too quiet — doubling (21 → 42 → 84 → 90) until the volume suffices — so adverse evidence ages out gradually instead of expiring at a cliff. Path 3 still exists, but it's realistic only after ~3 months of silence, by which point the stale evidence arguably should stop counting.

Does refilling the channel clear it?

Yes — there's a fourth, automatic path: liquidity recovery. If the channel's local_ratio climbs back to REBALANCE_TARGET (≥50%), the structural verdict is cleared regardless of earnings or failure history — a channel that's no longer depleted isn't a structural emergency. This is what retires the alarm when the inbound-fee discount (or any organic flow) actually refills the channel.

⚠️ Attention: the escape uses strong hysteresis — it trips below 20% local and only clears at ≥50%, so it can't flap on a channel hovering near the edge. It clears only the structural escalation; profit_capped (the budget cap) is unaffected, since earnings vs refill cost haven't changed. The flag and its timestamp clear within one 2h fee-loop cycle of recovery.

The short answer

The flag clears on the next run after the channel refills back to target, a successful (often operator-forced) rebalance, a real earnings recovery, the channel falling back to calibrating for lack of volume, or its failure evidence expiring (~90d, the quarterly re-probe). The one thing the auto-pipeline won't do is pay to refill a flagged channel while the flag stands — so if organic flow never brings it back, the fastest fixes remain yours: force a successful rebalance, or make the capital call the flag is asking for (open inbound / splice / close).

Like the thinking? Run it on your own node.