askrene: log reservation failures during getroutes
What changed, and why it matters
This change adds logging when the Core Lightning routing plugin (askrene) fails to remove a temporary channel-capacity reservation. Previously these failures were silently ignored, which could cause reservations to 'leak' and remain in place longer than intended. The patch only adds diagnostics; it does not fix the underlying removal failure or the leak itself.
Treat this as a defensive diagnostic improvement, not a complete fix. Investigate why reserve_remove() can fail and ensure stale reservations are cleaned up or accounted for. Consider adding recovery logic or assertions if reservation leaks can affect routing correctness or resource exhaustion.
Security signals we found
Resource leak: failed reservation removals can leave stale reservations behind
Silent failure pattern: prior code ignored reserve_remove() return value
Diagnostic-only patch: does not remediate the underlying failure path
Potential denial-of-service or routing degradation from leaked reservations
Evidence from the diff
In plugins/askrene/child/refine.c, destroy_reservations() now checks the return value of reserve_remove() and emits a LOG_BROKEN message with the amount and short channel id if removal fails. The commit message explicitly states that failed reservation removals can lead to reservation leaks. No logic is changed to handle or recover from the failure beyond logging.
Changed components
plugins/askrene/child/refine.caskrene routing plugin reservation cleanup pathInspect captured patch +9 / −2
diff --git a/plugins/askrene/child/refine.c b/plugins/askrene/child/refine.c
index 05adc78e..2556e487 100644
--- a/plugins/askrene/child/refine.c
+++ b/plugins/askrene/child/refine.c
@@ -27,8 +27,15 @@ static void get_scidd(const struct gossmap *gossmap,
static void destroy_reservations(struct reserve_hop *rhops, struct reserve_htable *reserved)
{
- for (size_t i = 0; i < tal_count(rhops); i++)
- reserve_remove(reserved, &rhops[i]);
+ for (size_t i = 0; i < tal_count(rhops); i++) {
+ if (!reserve_remove(reserved, &rhops[i])) {
+ child_log(tmpctx,
+ LOG_BROKEN,
+ "reserve_remove failed: %s on %s",
+ fmt_amount_msat(tmpctx, rhops[i].amount),
+ fmt_short_channel_id_dir(tmpctx, &rhops[i].scidd));
+ }
+ }
}
struct reserve_hop *new_reservations(const tal_t *ctx,
Why this scored 31/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.