plugin: watchtower: call start_watching() threadsafe
What changed, and why it matters
This is a small bug-fix patch for Electrum's watchtower plugin. It changes how a background monitoring task is started so it is scheduled on the correct event loop thread, preventing a crash when running Python with extra asyncio debugging enabled. The crash is a reliability issue, not a security vulnerability that can be exploited by an attacker.
Treat as a routine stability/reliability fix. No urgent security response is needed. Users running the watchtower plugin with PYTHONASYNCIODEBUG=1 will benefit from the corrected scheduling behavior.
Security signals we found
Thread-safety fix in asyncio task scheduling
Crash-only symptom (RuntimeError on plugin load)
No attacker-controlled input or privilege boundary crossed
No memory corruption, injection, or cryptographic weakness
Evidence from the diff
The commit replaces asyncio.ensure_future(self.watchtower.start_watching()) with asyncio.run_coroutine_threadsafe(self.watchtower.start_watching(), self.network.asyncio_loop). The original code created a task on the wrong event loop from a non-event-loop thread, which raises RuntimeError under PYTHONASYNCIODEBUG=1 and is generally unsafe. The fix schedules the coroutine onto the network’s asyncio loop in a thread-safe manner, matching the pattern already used for the WatchTowerServer task on the next line.
Changed components
electrum/plugins/watchtower/watchtower.pyWatchtowerPlugin initializationInspect captured patch +1 / −1
diff --git a/electrum/plugins/watchtower/watchtower.py b/electrum/plugins/watchtower/watchtower.py
index 6bb08a8..04212c2 100644
--- a/electrum/plugins/watchtower/watchtower.py
+++ b/electrum/plugins/watchtower/watchtower.py
@@ -56,7 +56,7 @@ class WatchtowerPlugin(BasePlugin):
return
self.watchtower = WatchTower(self.network)
- asyncio.ensure_future(self.watchtower.start_watching())
+ asyncio.run_coroutine_threadsafe(self.watchtower.start_watching(), self.network.asyncio_loop)
if watchtower_port := self.config.WATCHTOWER_SERVER_PORT:
self.server = WatchTowerServer(self.watchtower, self.network, watchtower_port)
asyncio.run_coroutine_threadsafe(self.network.taskgroup.spawn(self.server.run), self.network.asyncio_loop)
Why this scored 24/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.