Skip to content

Fix MSP status polls leaking into CLI session on tab entry - #2688

Merged
sensei-hacker merged 2 commits into
iNavFlight:maintenance-9.xfrom
sensei-hacker:fix-cli-tab-msp-polling-leak
Jul 20, 2026
Merged

Fix MSP status polls leaking into CLI session on tab entry#2688
sensei-hacker merged 2 commits into
iNavFlight:maintenance-9.xfrom
sensei-hacker:fix-cli-tab-msp-polling-leak

Conversation

@sensei-hacker

Copy link
Copy Markdown
Member

Summary

Fixes the CLI tab intermittently corrupting its own session on entry, visible as $X&lt$X&lt... garbage in the CLI output/input area (a leaked MSP2 request frame — < is the MSP TO_MWC direction byte).

Two independent bugs contributed to this, both closed here:

  1. cliTab.initialize() flush-then-flag race (tabs/cli.js): the MSP queue was flushed synchronously, but CONFIGURATOR.cliActive wasn't set true until inside the async cli.html import callback. periodicStatusUpdater's 300ms status-poll interval (kept alive across tab switches for the header status bar) checks cliActive before sending, but during that async gap it still saw false and queued sends that were never flushed again. Fix: set the flag as the very first statement in initialize(), before the flush and before the async import, closing the race entirely (JS is single-threaded).

    This also widens the window where cliActive can be true before the CLI DOM/session is confirmed ready. Re-audited every other cliActive consumer (CliAutoComplete.js, serial_backend.js, configurator_main.js) for timing assumptions; the tab-click handler's cliTab.exit() call needed an added cliValid check (it only becomes true once the FC has actually echoed CLI-mode confirmation) to avoid sending a stray exit\r before the FC has even been told to enter CLI mode.

  2. MSP._enqueue()'s duplicate-retry loop bypassing CLI mode (js/msp.js): found while live-testing fix LTM - Add telemetry support #1 against real hardware — the garbage still reproduced, well after CLI mode was fully confirmed. Root cause: mspQueue.put() rejects a message as a duplicate if the same MSP code is still awaiting a response (routine when round-trip time exceeds the poll interval, e.g. slower serial links). The rejected message retries every 150ms for up to 25 attempts, entirely independent of whoever originally called send_message() — and never re-checked CONFIGURATOR.cliActive on each retry. A poll deferred right before switching to CLI could still land several seconds into the session. Fix: check cliActive before every put() attempt, not just when deciding whether to schedule the next retry.

Testing

  • Added tests/cli-tab-msp-polling.test.mjs (4 tests): executes the real cliTab.initialize(), periodicStatusUpdater.run(), and MSP._enqueue()/mspQueue production code (via mechanical import-specifier substitution, not mocks/reimplementations) to reproduce and verify both races, plus two positive-control tests proving the mechanisms aren't vacuously passing.
  • Full suite: 49/49 passing (npm test).
  • Live-tested on real FC hardware (SPEDIXF405, USB serial @ 115200 baud) via Chrome DevTools: repeated CLI tab entry showed no leaked MSP frames over an 8+ second observation window (previously reproduced reliably); confirmed CLI exit correctly triggers the FC's normal reboot-on-exit behavior and the header status bar (MSP round trip, CPU load, heading/pitch/roll) resumes updating normally afterward.

Code Review

Reviewed with the inav-code-review agent in two passes (one per commit) — no CRITICAL or IMPORTANT issues outstanding; one MINOR comment-length nitpick addressed.

cliTab.initialize() flushed the MSP queue synchronously but only set
CONFIGURATOR.cliActive true inside the async cli.html import callback,
leaving a window where periodicStatusUpdater's 300ms poll still saw
cliActive as false and queued status requests that never got flushed
again. Those requests went out over the wire and corrupted the FC's
CLI output (visible as leaked MSP2 frame bytes). Move the flag to the
first line of initialize() so no interval tick can land in the gap.

Also guard the tab-click handler's cliTab.exit() call with cliValid in
addition to cliActive, since cliActive can now be true before the FC
has confirmed CLI mode was actually entered.
MSP.send_message()'s queue-put can be rejected as a duplicate when the
same MSP code is still awaiting a response (routine on a slow/loaded
serial link where round-trip exceeds the poll interval). The rejected
message retried every 150ms for up to 25 attempts without ever
re-checking CONFIGURATOR.cliActive, so a status poll deferred right
before switching to the CLI tab could still land several seconds
later, once the FC was already echoing raw bytes in CLI mode. Found by
reproducing the original bug's exact symptom live after the first fix
landed. Check cliActive before every attempt, not just the one made at
send_message() time.
@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@sonarqubecloud

Copy link
Copy Markdown

@sensei-hacker sensei-hacker added this to the 9.1 milestone Jul 20, 2026
@github-actions

Copy link
Copy Markdown

Configurator test build ready — commit 580f2ed

Download build artifacts for PR #2688

Available platforms (scroll to the Artifacts section at the bottom of the run page):

  • Windows x64 (ZIP, MSI) and x32 (ZIP, MSI)
  • macOS arm64 (ZIP, DMG) and x64 (ZIP, DMG)
  • Linux x64 (DEB, RPM, ZIP) and aarch64 (DEB, RPM, ZIP)

A GitHub login is required to download artifacts. Build is for testing only.

@sensei-hacker
sensei-hacker merged commit d00f91b into iNavFlight:maintenance-9.x Jul 20, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant