Repository navigation
api: a timed-out SD LIST is indistinguishable from an empty SD card #396
Description
Summary
GetSdCardFilesAsync returns an empty list both when the SD card genuinely has no files and when the device never answered. Clients cannot tell the two apart, so a device that is unreachable — or merely slow — is reported to users as a healthy, empty card.
Trace (v1.3.0)
DaqifiDevice.ExecuteTextCommandCoreAsyncbreaks out of its phase-1 wait afterresponseTimeoutMsand returns the lines collected so far — an empty collection — rather than throwing.DaqifiStreamingDevice.ThrowIfSdCardListErrorthen treats that as success; its own closing comment says "No error lines and no content lines — empty directory. Caller continues."SdCardFileListParser.ParseFileList([])yields an empty list.
Net: timeout and empty-directory are the same observable result.
Why it matters more now
Over USB this was mostly masked — an unplug faults the serial transport and Core throws. With SD file transfer over WiFi (#347) the ambiguity became reachable in normal operation: a silently dead TCP link (see #394 — no read-error hook or keepalive, so IsConnected stays true) or plain WiFi congestion that pushes the reply past the ~3 s phase-1 window both produce a clean "empty card".
Downstream, the Avalonia port rendered "SD card OK · 0 files" for an unreachable device holding data (daqifi-avalonia#2).
Current downstream mitigation, and why it is only partial
The port now corroborates a zero-length list with GetSdCardStorageAsync, whose parser cannot read a missing reply as success and throws. That converts "always wrong on a dead link" into "wrong only when the LIST was lost but the link recovered within one round-trip".
It cannot do better: a successful storage query proves the device answered a later request, not that the LIST was complete. A truncated or selectively-lost LIST still reads as a short/empty directory. The complete fix has to be in Core, which is the only layer that knows a reply never arrived.
Suggested fix
Make "no reply" distinguishable from "empty reply" on the text path — any of:
- Throw on phase-1 timeout when zero lines were collected (a
TimeoutException/DeviceNotRespondingException), rather than returning an empty collection. Cleanest, but a behavior change for any caller relying on empty-on-timeout. - Return a result type carrying
TimedOut/Completedalongside the lines, and haveGetSdCardFilesAsyncthrow when the list came back timed-out-and-empty. - Terminator-aware LIST parsing — if the firmware emits an end-of-listing marker, require it before treating a listing as complete; absence means truncated, not empty.
(1) or (2) also fixes the truncation case, which no downstream mitigation can reach.
Filed from a downstream audit of the Avalonia port; companion to #394 and #395.
Activity
fix(sdcard): drop pre-command lines and widen the SD listing completi…
fix(device): scope the terminator short-circuit to the connect-time i…