Failed replay attempts after which flushOutbox stops treating a queued
send as transiently-blocked and DROPS it (SDK 0.50.0).
WHY THIS EXISTS. The queue is FIFO and a failing head item holds up
everything behind it — correct for a message queue, where reordering a
conversation is worse than delaying it, but it assumes the head item can
eventually succeed. One class breaks that assumption: a request the HOST
refuses before it reaches the API (a body over the platform's ~4.5MB limit —
in practice an un-downscaled photo) is rejected without CORS headers, so
cross-origin fetch REJECTS and the SDK sees status: 0 with no code —
indistinguishable from the network being down. sendText therefore queues
it, and it fails identically on every flush. For an AMBIGUOUS item the
30-minute age bound above eventually dropped it; for a provably-unsent one
there was no exit at all and it replayed on every reconnect forever.
ALL THREE CONDITIONS ARE LOAD-BEARING, because dropping a real message is a
worse failure than delaying one:
status === 0 only. Every readable status (5xx, 429, 409/408/423/425)
means the server saw the request and owns the condition; those still
wait indefinitely.
Attempts are counted only while navigator.onLine !== false. A flapping
or absent connection explains the failure on its own and must not spend
the budget — a device offline overnight comes back with 0 attempts.
The item must ALSO be older than OUTBOX_AMBIGUOUS_MAX_AGE_MS. A short
outage can produce several reconnects, hence several attempts, in a few
minutes; requiring age too means no realistic outage reaches the drop.
A drop is announced on console.warn and fires onOutboxChange.
Failed replay attempts after which
flushOutboxstops treating a queued send as transiently-blocked and DROPS it (SDK 0.50.0).WHY THIS EXISTS. The queue is FIFO and a failing head item holds up everything behind it — correct for a message queue, where reordering a conversation is worse than delaying it, but it assumes the head item can eventually succeed. One class breaks that assumption: a request the HOST refuses before it reaches the API (a body over the platform's ~4.5MB limit — in practice an un-downscaled photo) is rejected without CORS headers, so cross-origin
fetchREJECTS and the SDK seesstatus: 0with nocode— indistinguishable from the network being down.sendTexttherefore queues it, and it fails identically on every flush. For an AMBIGUOUS item the 30-minute age bound above eventually dropped it; for a provably-unsent one there was no exit at all and it replayed on every reconnect forever.ALL THREE CONDITIONS ARE LOAD-BEARING, because dropping a real message is a worse failure than delaying one:
status === 0only. Every readable status (5xx, 429, 409/408/423/425) means the server saw the request and owns the condition; those still wait indefinitely.navigator.onLine !== false. A flapping or absent connection explains the failure on its own and must not spend the budget — a device offline overnight comes back with 0 attempts.OUTBOX_AMBIGUOUS_MAX_AGE_MS. A short outage can produce several reconnects, hence several attempts, in a few minutes; requiring age too means no realistic outage reaches the drop.A drop is announced on
console.warnand firesonOutboxChange.