fix(traceroute): prevent fictitious direct-connection segments for target-node traceroutes (#3622)#3635
Conversation
…rget-node traceroutes (#3622) When MeshMonitor is connected to the TARGET node (L) of an incoming traceroute from node A: 1. L's firmware receives the REQUEST (requestId=0) and immediately emits a RESPONSE (requestId≠0) before relay nodes populate routeBack. 2. OLD behaviour: - processTracerouteMessage processed the REQUEST, saving a record with wrong from/to orientation and routeBack='[]'. A broad guard then skipped the RESPONSE (fromNum === localNode), so users saw neither the correct traceroute NOR the return path — only a fictitious A→L direct line on the map overlay. - segmentsForTraceroute in useTracerouteAnalysis always built backPath=[responder, requester] even when routeBack AND snrBack were both empty, drawing the direct segment. 3. NEW behaviour (three-part fix): a. meshtasticManager.ts — processTracerouteMessage now inspects decoded.requestId: REQUEST packets (requestId=0) are skipped entirely; RESPONSE packets from localNode are saved to the DB but skip route-segment creation since routeBack is not yet populated. b. useTracerouteAnalysis.ts — segmentsForTraceroute guards the back-path loop: skipped when both routeBack AND snrBack are empty (unresolved return path). Preserved when snrBack has data (genuine direct hop). c. traceroute.tsx — formatTracerouteRoute returns '(No return path data)' when both route and snr parse to empty arrays, preventing the fictitious A→B line in TracerouteHistoryModal. Tests: add useTracerouteAnalysis.emptyBack.test.ts (4 tests) and traceroute.emptyBack.test.ts (4 tests) covering the new guards; update meshtasticManager.traceroute-hops.test.ts to set requestId on RESPONSE test packets so they pass the new REQUEST-skip guard. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZwPi1Waokuoyv8pppjtCA
|
Claude encountered an error —— View job I'll analyze this and get back to you. |
|
Claude finished @Yeraze's task —— View job PR Review
Overall AssessmentThis is a well-structured, carefully reasoned fix for a subtle dual-bug. The root causes are correctly identified, the three-part fix addresses each layer independently, and the test coverage is solid. A few notes below.
|
…zed localNodeInfo race Address reviewer feedback on PR #3635: - meshtasticManager.ts: replace the sole `isLocalNodeResponse` guard on the insertRouteSegment block with a dual check: `!isLocalNodeResponse && !isEmptyReturnPath`. The second condition (`routeBack.length === 0 && snrBack.length === 0`) catches the same fictitious-segment scenario even when `localNodeInfo` is null (connection not yet fully initialized), closing a pre-existing race window that the prior fix left open. - traceroute.emptyBack.test.ts: correct comment for the intermediate hop literal 3221291011 from `(0xcccc0003)` to `(0xc0010003)`. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZwPi1Waokuoyv8pppjtCA
|
Claude finished @Yeraze's task —— View job PR Review
Overall AssessmentThis is a well-structured, carefully reasoned fix for a subtle dual-bug. The root causes are correctly identified, the three-part fix addresses each layer independently, and the test coverage is solid. A few notes below.
|
Add a debug log when the isEmptyReturnPath guard skips segment creation for a non-local node (e.g. old firmware without snrBack, or an unresolved return path). This distinguishes the remote-node case from the local-node response case in production logs, which previously both silently skipped. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UZwPi1Waokuoyv8pppjtCA
|
Claude finished @Yeraze's task —— View job PR Review
Overall AssessmentThis is a well-structured, carefully reasoned fix for a subtle dual-bug. The root causes are correctly identified, the three-part fix addresses each layer independently, and the test coverage is solid. A few notes below.
|
Summary
Fixes #3622 — two interacting bugs when MeshMonitor is connected to the target node (L) of an incoming traceroute from A:
requestId=0) was being processed and saved withrouteBack=[].segmentsForTraceroutethen builtbackPath=[A, L]— a fictitious direct-connection line on the map overlay, even when the actual path went through relay nodes.if fromNum === localNode → return) that blocked saving the RESPONSE entirely, making the traceroute invisible to the user.Three-part fix
meshtasticManager.ts— Replace broadfrom===localNodeguard withrequestId-based logic:requestId=0(REQUEST): skip entirely — firmware will respond, record comes when RESPONSE arrivesrequestId≠0from localNode (RESPONSE): save the DB record but skip route-segment creation (routeBack is not yet populated by relay nodes)useTracerouteAnalysis.ts— GuardsegmentsForTracerouteback-path loop:routeBackandsnrBackare empty (unresolved return path, not a genuine direct hop)snrBackhas data (genuine single-hop with measured SNR)utils/traceroute.tsx— GuardformatTracerouteRoutefor history modal:'(No return path data)'when route and snr both parse to empty arraysTest plan
src/hooks/useTracerouteAnalysis.emptyBack.test.ts— 4 new tests: empty routeBack+snrBack → no segment; snrBack present → segment drawn; forward path unaffected; populated routeBack workssrc/utils/traceroute.emptyBack.test.ts— 4 new tests: empty arrays → sentinel string; snr present → renders; null → existing behavior; multi-hop → renderssrc/server/meshtasticManager.traceroute-hops.test.ts— updated packet fixtures to includerequestId: 12345(responses, not requests) so all 7 existing tests pass the new REQUEST-skip guardmqttBrokerManagerfailures are unrelated to this change)🤖 Generated with Claude Code
https://claude.ai/code/session_01UZwPi1Waokuoyv8pppjtCA
Generated by Claude Code