From 0413d00825fea5e3aa2230278852bb2292bbb774 Mon Sep 17 00:00:00 2001 From: Nox Date: Wed, 3 Jun 2026 22:50:56 +0800 Subject: [PATCH 1/3] Handle ROUTING_APP error response in onResponseTraceRoute Previously, receiving a ROUTING_APP packet in response to a traceroute would cause the function to attempt parsing it as a RouteDiscovery payload, resulting in a crash or silent failure followed by a timeout. This mirrors the error handling already present in onResponseTelemetry and onResponseWaypoint, but displays the specific errorReason instead of a hardcoded firmware version message, since traceroute is a diagnostic tool where the exact failure reason is more valuable. Co-Authored-By: Claude Sonnet 4.6 --- meshtastic/mesh_interface.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/meshtastic/mesh_interface.py b/meshtastic/mesh_interface.py index 5dfb393b7..d8083c654 100644 --- a/meshtastic/mesh_interface.py +++ b/meshtastic/mesh_interface.py @@ -687,6 +687,12 @@ def sendTraceRoute( def onResponseTraceRoute(self, p: dict): """on response for trace route""" + if p["decoded"]["portnum"] == "ROUTING_APP": + error = p["decoded"]["routing"]["errorReason"] + print(f"Traceroute failed: {error}") + self._acknowledgment.receivedTraceRoute = True + return + UNK_SNR = -128 # Value representing unknown SNR routeDiscovery = mesh_pb2.RouteDiscovery() From c71d3df3322cb2168395ff29f753c08cd43d8045 Mon Sep 17 00:00:00 2001 From: Nox Date: Thu, 4 Jun 2026 12:45:59 +0800 Subject: [PATCH 2/3] Add unit test for onResponseTraceRoute ROUTING_APP error handling Co-Authored-By: Claude Sonnet 4.6 --- meshtastic/tests/test_mesh_interface.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/meshtastic/tests/test_mesh_interface.py b/meshtastic/tests/test_mesh_interface.py index 8d53628fb..d002c187c 100644 --- a/meshtastic/tests/test_mesh_interface.py +++ b/meshtastic/tests/test_mesh_interface.py @@ -757,3 +757,23 @@ def test_timeago_fuzz(seconds): """Fuzz _timeago to ensure it works with any integer""" val = _timeago(seconds) assert re.match(r"(now|\d+ (secs?|mins?|hours?|days?|months?|years?))", val) + + +@pytest.mark.unit +@pytest.mark.usefixtures("reset_mt_config") +def test_onResponseTraceRoute_routing_error(capsys): + """Test that onResponseTraceRoute handles ROUTING_APP error packets correctly.""" + iface = MeshInterface(noProto=True) + + packet = { + "decoded": { + "portnum": "ROUTING_APP", + "routing": {"errorReason": "MAX_RETRANSMIT"}, + } + } + + iface.onResponseTraceRoute(packet) + + assert iface._acknowledgment.receivedTraceRoute is True + out, _ = capsys.readouterr() + assert "Traceroute failed: MAX_RETRANSMIT" in out From 953d01206bbc5f3ee3cee32422a359cd3f459e63 Mon Sep 17 00:00:00 2001 From: Nox Date: Thu, 4 Jun 2026 21:03:49 +0800 Subject: [PATCH 3/3] Handle NONE errorReason in onResponseTraceRoute and add regression test ROUTING_APP with errorReason NONE is an ACK (success), not an error. Only print failure message for non-NONE error reasons. Co-Authored-By: Claude Sonnet 4.6 --- meshtastic/mesh_interface.py | 3 ++- meshtastic/tests/test_mesh_interface.py | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/meshtastic/mesh_interface.py b/meshtastic/mesh_interface.py index d8083c654..1a7dd6eae 100644 --- a/meshtastic/mesh_interface.py +++ b/meshtastic/mesh_interface.py @@ -689,7 +689,8 @@ def onResponseTraceRoute(self, p: dict): """on response for trace route""" if p["decoded"]["portnum"] == "ROUTING_APP": error = p["decoded"]["routing"]["errorReason"] - print(f"Traceroute failed: {error}") + if error != "NONE": + print(f"Traceroute failed: {error}") self._acknowledgment.receivedTraceRoute = True return diff --git a/meshtastic/tests/test_mesh_interface.py b/meshtastic/tests/test_mesh_interface.py index d002c187c..7f0a9f5b1 100644 --- a/meshtastic/tests/test_mesh_interface.py +++ b/meshtastic/tests/test_mesh_interface.py @@ -777,3 +777,23 @@ def test_onResponseTraceRoute_routing_error(capsys): assert iface._acknowledgment.receivedTraceRoute is True out, _ = capsys.readouterr() assert "Traceroute failed: MAX_RETRANSMIT" in out + + +@pytest.mark.unit +@pytest.mark.usefixtures("reset_mt_config") +def test_onResponseTraceRoute_routing_none(capsys): + """Test that onResponseTraceRoute does not print error for ROUTING_APP with NONE errorReason.""" + iface = MeshInterface(noProto=True) + + packet = { + "decoded": { + "portnum": "ROUTING_APP", + "routing": {"errorReason": "NONE"}, + } + } + + iface.onResponseTraceRoute(packet) + + assert iface._acknowledgment.receivedTraceRoute is True + out, _ = capsys.readouterr() + assert "Traceroute failed" not in out