client: unconfirmed sends are visible and recovered; watchdog for a dead connection #123

Merged
void merged 4 commits from void/resume-reload into dev 2026-11-12 21:35:48 +00:00
Member

Why messages looked sent and vanished (no PUT /send reached the server, rooms unencrypted): matrix-js-sdk adds a local echo on sendMessage and the timeline drew it like a delivered message at every status; sends have no timeout (sync polls do), so on a dead/half-open connection a send hangs SENDING with no request out; a reload drops the echo.

  • timeline hides unconfirmed local echoes; the outbox row (sending / still sending 30s / interrupted by reload / failed, Resend, Discard) is the one place they show; uploads and stickers go through it too; resend takes a fresh txn id while the SDK holds the failed echo
  • watchdog: a send from this page pending >=20s while visible and online triggers one guarded reload (once/60s); after reload, interrupted entries are resent under their own txn id
  • resumeSync only nudges; it no longer calls stopClient/startClient (closes the rust crypto for good: 'null pointer passed to rust' on later encrypted sends)
  • scroll to bottom when a hidden echo is confirmed

Soul passes: three; findings applied. Not covered: uploads/edits in flight are lost on the watchdog reload; Resend of a slow send can duplicate; two tabs can resend the same entry; plaintext drafts in localStorage. The 20s threshold is a trade-off for the operator.

🤖 Generated with Claude Code

Why messages looked sent and vanished (no PUT /send reached the server, rooms unencrypted): matrix-js-sdk adds a local echo on sendMessage and the timeline drew it like a delivered message at every status; sends have no timeout (sync polls do), so on a dead/half-open connection a send hangs SENDING with no request out; a reload drops the echo. - timeline hides unconfirmed local echoes; the outbox row (sending / still sending 30s / interrupted by reload / failed, Resend, Discard) is the one place they show; uploads and stickers go through it too; resend takes a fresh txn id while the SDK holds the failed echo - watchdog: a send from this page pending >=20s while visible and online triggers one guarded reload (once/60s); after reload, interrupted entries are resent under their own txn id - resumeSync only nudges; it no longer calls stopClient/startClient (closes the rust crypto for good: 'null pointer passed to rust' on later encrypted sends) - scroll to bottom when a hidden echo is confirmed Soul passes: three; findings applied. Not covered: uploads/edits in flight are lost on the watchdog reload; Resend of a slow send can duplicate; two tabs can resend the same entry; plaintext drafts in localStorage. The 20s threshold is a trade-off for the operator. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The SDK puts a local echo in the live timeline on sendMessage and marks it
NOT_SENT on failure; the timeline rendered it like any delivered message and a
reload dropped it, so a send killed with the page looked sent and vanished.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Sends have no timeout and sync polls do, so a dead connection leaves a send
SENDING forever with no request reaching the server while its echo looked
delivered. A watchdog (send pending past 20 s, sync ERROR/STOPPED for 30 s)
calls a guarded reload; what the reload interrupts is sent again under its
own txn id. Uploads and stickers go through the outbox. Resend takes a fresh
txn id while the SDK still holds the failed echo. The resume-sync recovery no
longer calls stopClient/startClient, which closes the rust crypto for good
("null pointer passed to rust" on later encrypted sends).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Soul pass on 77edebff: the watchdog no longer counts interrupted entries from
a previous page (reload loop at startup), no longer reloads on sync ERROR/STOPPED
(js-sdk recovers offline by itself) or while offline, refuses instead of
throwing when sessionStorage is unusable, startClient is idempotent, and the
slow-send label says Resend may duplicate. resumeSync drops its health check
and recover hook: nudge only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
initMatrix: resolve sessionStorage inside the guard's try; ownPendingAges tested
All checks were successful
Build pull request / Build pull request (pull_request) Successful in 3m39s
bb6391a70b
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
void merged commit b6b1d522ab into dev 2026-11-12 21:35:48 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
GameCult/Ratatoskr!123
No description provided.