fix: detect and skip Limitless firmware diagnostic pages blocking flash sync#5924
Conversation
Greptile SummaryThis PR fixes a flash sync stall on Limitless Pendant firmware v1.1.20, where diagnostic pages (RTOS snapshots, firmware logs, BLE events) written in the same protobuf page format as audio were causing Key changes:
The critical invariant — audio pages are never ACKed without being processed — is preserved: Confidence Score: 5/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Flash page received\nopusFrames = extractOpusFrames] --> B{opusFrames.isNotEmpty?}
B -- Yes --> C[Add to _completedFlashPages\nwith audio frames]
B -- No --> D[_hasAudioSubfields\nparse 0x1a wrappers]
D --> E{Any 0x12\nsubfield found?}
E -- Yes / parse error --> F[Genuine audio parse failure\nlogWarning + hex dump\nDo NOT add to completedPages]
E -- No 0x12 found --> G[Diagnostic page\nlogEvent + add to _completedFlashPages\nwith empty opus_frames]
C --> H[extractFramesWithSessionInfo]
G --> H
H --> I{allFrames.isEmpty?}
I -- No --> J[Return frames + max_index]
I -- Yes --> K[Return empty frames + max_index\nlog diagnostic_only event]
K --> L{maxIndex == null?}
L -- Yes --> M[logWarning: diagnostic_no_index]
L -- No --> N[flash_page_wal_sync:\nlastProcessedIndex updated]
J --> N
N --> O{accumulatedFrames\n.isEmpty AND timer elapsed?}
O -- Yes --> P[Send periodic diagnostic ACK\nlastSaveTime = now]
O -- No --> Q{shouldSave AND\naudio frames?}
Q -- Yes --> R[_saveBatchToFile\nthen ACK lastProcessedIndex]
Q -- No --> S[Continue loop]
P --> S
R --> S
Reviews (2): Last reviewed commit: "fix: warn when diagnostic-only batch has..." | Re-trigger Greptile |
…udioSubfields Replace the naive pos++ for non-0x1a top-level bytes with correct wire-type-based skipping. Prevents misalignment when diagnostic pages contain top-level varint, length-delimited, or fixed-width fields before the 0x1a wrappers. https://claude.ai/code/session_01EEe9uEkE2iHmAf5fRmEL7d
Log a warning when extractFramesWithSessionInfo() processes a diagnostic-only batch but maxIndex is null, indicating pages lacked index metadata and the ACK cannot advance. https://claude.ai/code/session_01EEe9uEkE2iHmAf5fRmEL7d
|
Ready for maintainer review. Implemented two additional fixes based upon greptile bot review. |
|
@greptile-apps re-review |
|
@mdmohsin7 - this is my first PR. Anything else you need for this? |
|
Thanks for this PR @brightshore-ventures, I'll check and get back to you if anything is needed |
Problem
The Limitless Pendant firmware (v1.1.20) writes diagnostic logs to flash storage using the same protobuf page format as audio recordings. The Omi app's Opus parser returns zero frames for these pages because they contain no audio — only RTOS thread snapshots, firmware log messages, and BLE events.
The batch downloader cannot ACK past zero-frame pages, so
oldestPagegets permanently stuck and flash storage fills monotonically until the device is factory reset.No audio is lost. These pages were never recordings — they're firmware diagnostics from idle sessions, boot sequences, and sync operations.
Related to #5733 (S2) and #5154.
Root Cause
Confirmed by capturing a failed sync simultaneously from both sides (Omi app debug log + nRF Connect BLE packet capture) and reassembling all 70 failing pages from raw BLE traffic.
Audio pages use protobuf field
0x12inside0x1awrappers. Diagnostic pages use fields0x4a/0x52/0x5a/0x62instead. The parser looks for0x12, never finds it, returns zero frames, and the ACK stalls.Example page contents from the firmware:
<inf> scheduler_ble: Recording state set to NOT_RECORDING<inf> scheduler_ble: Device mode set to IDLE_MODE<inf> bt_data_comm: Finished batch upload of flash_page(firmware logging its own sync)storage_thread_,audio_thread_ha,poll_thread_han)Changes
Two files.
app/lib/services/devices/limitless_connection.dartAdded
_hasAudioSubfields()— walks0x1aprotobuf wrappers checking for0x12(audio) subfields. On parse error, conservatively returnstrueto avoid ACKing unclassified pages.Modified zero-frame handler (line ~300) — when a page has no audio subfields, classifies it as diagnostic, adds it to
_completedFlashPageswith its index (enabling ACK advancement), and logs as INFO instead of WARN. Pages WITH audio subfields that still return zero frames are logged as WARN with a firstBytes hex dump for future debugging.Modified
extractFramesWithSessionInfo()— previously returnednullwhen all frames were empty, blocking the caller from seeingmax_index. Now returns a valid result with empty frames andmax_indexso the ACK can advance past diagnostic-only batches.Proper protobuf field skipping in outer loop — the
_hasAudioSubfields()outer loop now reads wire types from non-0x1atag bytes and advances past their full payloads (varint, length-delimited, fixed32, fixed64) instead of stepping byte-by-byte. Prevents false-positive audio classification when diagnostic payload bytes happen to contain0x1a.Warning log for null maxIndex — when a diagnostic-only batch has no page indices, logs a warning so the edge case is visible rather than silently degrading to stuck-ACK behavior.
app/lib/services/wals/flash_page_wal_sync.dart_persistBatchDurationtimer. Gated onpageData != nullto avoid redundant ACKs during idle iterations.Safety
The critical invariant: audio pages are never ACKed without being processed. The pendant permanently deletes pages after ACK.
_hasAudioSubfields()returnstrue(audio) on any parse error — false positives are safe, false negatives would delete audio0x12subfields across all0x1awrappers are classified as diagnostic0x12that still return zero frames) keep the existing behavior — logged as warnings, not ACKedTesting
Verified the protobuf field analysis against 70 failing pages (42943–43012) reassembled from raw BLE capture data. Every page confirmed to contain only firmware diagnostics, zero audio subfields.
Environment