fix: keep unhandled service-thread errors from killing the root service #37
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/guard-service-threads"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #17.
Problem
TunnelThread.run's onlyexcept KeyErrorwas the OpenVPN dispatch fallback, not a guard: anything else raised while establishing a tunnel reached the top of the QThread and PyQt5 aborted the root D-Bus service, dropping every active tunnel. The fallback also rerouted aKeyErrorraised anywhere insidewireguard()intoopenvpn()with a WireGuard server dict.14dd13afixed this pattern for the importer only.Fix
One pattern, as the issue proposed:
GuardedThread(aqomui/threads.py) ownsrun(), subclasses implementwork()andreport_failure(), and nothing reaches the top of the thread. Both QThreads the service owns are on it --AddServersmigrated with no behavior change,TunnelThreadnewly guarded. A failed tunnel attempt now logs the cause, deletes the outbound allow rule(s) it inserted (previously a failed attempt left a permanent ACCEPT hole in the killswitch), and reportsconn_attempt_failed(_bypass) -- statuses the GUI already answers with cleanup and a notification.Along the way:
wg()'s local handler emitted"fail", which the GUI cannot dispatch (getattron the status name -- no such method), and logged nothing; deleted in favor of the guard. The two related swallows from the issue are narrowed:allow_dest_ipno longer labels every failure "not a valid ip address", and the hop-ip bareexceptcatches only what the no-hop case produces.Verification
tests/test_tunnel_failure.py(no root, network or account) -- verified to fail 8 checks against the unfixed code, including the wireguard-to-openvpn reroute, all passing after.tests/test_import_failure.pyunchanged and still passing.Known gaps (pre-existing, out of scope here)
threading.Threadhelpersopenvpn()starts (hop/ssl/ssh) are not QThreads: an exception there doesn't abort the service, but the hop case leaveswork()waiting onconnect_statusforever with no status reported.check()harness is now copy-pasted across five test scripts; worth extracting if a sixth appears.🤖 Generated with Claude Code
TunnelThread.run caught only KeyError, and that except was the OpenVPN dispatch fallback, not a guard -- anything else raised while establishing a tunnel reached the top of the QThread and PyQt5 aborted the root service, dropping every active tunnel. The fallback also rerouted a KeyError raised anywhere inside wireguard() into openvpn() with a WireGuard server dict. TunnelThread now runs on GuardedThread: the dispatch keys on server_dict.get("tunnel"), so a missing key still means OpenVPN, and an escaped error ends as conn_attempt_failed -- the status the GUI already answers with cleanup and a notification. report_failure also deletes the outbound allow rule(s) the attempt inserted, which nothing else takes back on this path. tests/test_tunnel_failure.py covers it (no root, network or account) and fails against the unfixed code, including the wireguard-to-openvpn reroute. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>