-
Notifications
You must be signed in to change notification settings - Fork 0
Truncated message frames are dropped silently #35
Description
SYNC_NEXT_MESSAGE has already popped the message off the node by the time the reply is parsed, so
a frame the app cannot read is a message that no longer exists anywhere. The drain drops it without
a word and immediately asks for the next one:
model::Message msg = parseMessageTail(r, snrQ4, channelIndex); if (r.ok()) Q_EMIT messageReceived(msg); // and if it is not ok, nothing at all
Reader is safe past the end by design — every accessor zeroes and sets the error flag rather than
crashing — but "safe" here only means "does not crash". Nothing acts on ok() being false: no
signal, no notice, no record, and requestSync() runs regardless, so the loss is invisible from the
UI and from the logs alike.
This is the parse-side twin of #5. That issue closed the storage half — a failed write now stops the
drain and holds an error on screen — which leaves this as the one remaining path where the app
destroys a message silently.
Worth noting what is and is not detectable. Reader::rest() is take(remaining()) and so can never
fail, meaning a body truncated mid-text parses as a perfectly good short message; what ok()
actually catches is a frame too short for the fixed header (SNR, reserved, slot, path length,
txt_type, timestamp). Only the second is recoverable, and only the second is in scope here.
The answer must not simply copy #5's. A storage fault is transient and worth stopping for — the
backlog keeps until it is fixed. A malformed frame is not: it will be exactly as malformed on the
next attempt, so stopping the drain on one would wedge collection behind a single bad message and
strand the rest of the backlog, which is a worse outcome than the bug. Record it, say so, keep
draining.
References:
src/protocol/client.cpp:529— a channel message that fails to parse is dropped on the floor.src/protocol/client.cpp:536— the same for a direct message.src/protocol/client.cpp:544— the drain asks for the next message either way.src/protocol/client.cpp:551—parseMessageTail, where the fixed header is read.src/protocol/frame_codec.cpp:122—rest()cannot fail, which is what bounds the scope above.
Tasks:
- Report an unparseable message frame out of
CompanionClientrather than returning silently,
carrying the raw payload so a caller can do something with it. - Keep the bytes: write the payload to the orphan conversation
History::orphanChannelalready
provides (Make message ingestion durable and observable #5 ), so an undecodable message is still recoverable by hand. - Tell the user a message arrived and could not be read, in the same place a storage fault is
reported. - Keep draining afterwards, and cover the decision with a comment — a bad frame does not get
better on a retry, and one must not strand the backlog behind it. - Decide whether a truncated body is worth detecting at all, given the wire carries no length
for it, and write the answer down either way.
Depends on #5 for the orphan conversation and for the notice line it would report through; both have
landed.