From ff615965982079c5aae23f9f1736e5f394c2b0c1 Mon Sep 17 00:00:00 2001 From: f321x Date: Fri, 19 Dec 2025 14:39:26 +0100 Subject: [PATCH 1/2] lnpeer: fix callback exception handler The done_callback for the callback tasks in _run_htlc_switch_iteration tried to access mpp_sets by key but they might already have been deleted when the callback is called, causing an KeyError. Instead forward the exceptions to the crash reporter so we get notice of them and they get logged correctly. ``` 20251219T131356.946565Z | ERROR | asyncio | Exception in callback Peer._run_htlc_switch_iteration..() at /home/user/code/electrum-fork/electrum/lnpeer.py:2907 handle: .() at /home/user/code/electrum-fork/electrum/lnpeer.py:2907 created at /usr/lib64/python3.14/asyncio/events.py:94> source_traceback: Object created at (most recent call last): File "/usr/lib64/python3.14/threading.py", line 1082, in _bootstrap_inner self._context.run(self.run) File "/home/user/code/electrum-fork/electrum/util.py", line 1145, in run_with_except_hook run_original(*args2, **kwargs2) File "/usr/lib64/python3.14/threading.py", line 1024, in run self._target(*self._args, **self._kwargs) File "/home/user/code/electrum-fork/electrum/util.py", line 1705, in run_event_loop loop.run_until_complete(stopping_fut) File "/usr/lib64/python3.14/asyncio/base_events.py", line 706, in run_until_complete self.run_forever() File "/usr/lib64/python3.14/asyncio/base_events.py", line 677, in run_forever self._run_once() File "/usr/lib64/python3.14/asyncio/base_events.py", line 2038, in _run_once handle._run() File "/usr/lib64/python3.14/asyncio/events.py", line 94, in _run self._context.run(self._callback, *self._args) Traceback (most recent call last): File "/usr/lib64/python3.14/asyncio/events.py", line 94, in _run self._context.run(self._callback, *self._args) ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/home/user/code/electrum-fork/electrum/lnpeer.py", line 2909, in f"{self.lnworker.received_mpp_htlcs[pk]=}", exc_info=t.exception()) if t.exception() else None ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^ KeyError: '0000980000010001:1' ``` --- electrum/lnpeer.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index e241e3f31..39a17db13 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -2901,10 +2901,8 @@ class Peer(Logger, EventListener): self._fulfill_htlc_set(payment_key, preimage) if callback: task = asyncio.create_task(callback()) - task.add_done_callback( # log exceptions occurring in callback - lambda t, pk=payment_key: self.logger.exception( - f"cb failed: " - f"{self.lnworker.received_mpp_htlcs[pk]=}", exc_info=t.exception()) if t.exception() else None + task.add_done_callback( # handle exceptions occurring in callback + lambda t: (util.send_exception_to_crash_reporter(t.exception()) if t.exception() else None) ) if len(self.lnworker.received_mpp_htlcs[payment_key].htlcs) == 0: From 14977e4ceefca25b466d7812d134deb9daf481db Mon Sep 17 00:00:00 2001 From: f321x Date: Fri, 19 Dec 2025 14:55:40 +0100 Subject: [PATCH 2/2] lnpeer: fix callback type hint asyncio.create_task expects a Coroutine, not all Awaitables are Coroutines. --- electrum/lnpeer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/electrum/lnpeer.py b/electrum/lnpeer.py index 39a17db13..9c2731c60 100644 --- a/electrum/lnpeer.py +++ b/electrum/lnpeer.py @@ -8,7 +8,7 @@ from collections import OrderedDict, defaultdict import asyncio import os import time -from typing import Tuple, Dict, TYPE_CHECKING, Optional, Union, Set, Callable, Awaitable, List +from typing import Tuple, Dict, TYPE_CHECKING, Optional, Union, Set, Callable, Coroutine, List, Any from datetime import datetime import functools from functools import partial @@ -2972,7 +2972,7 @@ class Peer(Logger, EventListener): ) -> Tuple[ Optional[Union[OnionRoutingFailure, OnionFailureCode, bytes]], # error types used to fail the set Optional[bytes], # preimage to settle the set - Optional[Callable[[], Awaitable[None]]], # callback + Optional[Callable[[], Coroutine[Any, Any, None]]], # callback ]: """ Returns what to do next with the given set of htlcs: