Describe the bug
When a wallet has used up its Suite Sync storage limit and the user edits a label, the relay rejects the write (ProtocolQuotaError). Suite reacts correctly and tops the wallet's limit up from the device pool — but the rejected label is never sent again. It stays local only, with no error shown, until the relay connection is re-established (app restart, or re-saving the relay URL in debug settings). Labels edited after the top-up sync normally, so the user silently loses exactly the one edit that hit the limit.
Info:
- Platform: web and desktop (both reproduced; mobile not tested but shares the same sync code)
- Browser: Chromium 151 (Playwright)
- Suite Version: 26.10.0 (develop)
- Firmware Version: T3W1 emulator, latest via trezor-user-env
- OS: Linux
Steps to reproduce
Realistic:
- Enable Suite Sync and label something so the wallet gets its first storage allocation.
- Fill the wallet's allocation (roughly 10 KB of label edits), then edit one more label.
- Observe on a second device / fresh client: the label from step 2 never arrives, while any label edited afterwards does.
- Restart the app — the missing label now arrives.
Fast, deterministic (e2e infra on develop, #32070):
yarn workspace @trezor/suite-e2e docker:suite-sync
- Run
suite/e2e/tests/metadata/suite-sync/quota-manager.test.ts — the top-up test shrinks the wallet limit in the quota DB, writes a label, verifies the top-up happened, and fails on its last step because the label never reaches the relay.
Without Suite (bare Evolu client, proves it is client library behavior): branch quota-dead-sync-repro, then
cd suite/e2e && yarn tsx scripts/reproduce-quota-dead-sync.ts
8 self-verifying checks, exit code 0 when all pass.
Expected behavior
After a successful quota top-up, the rejected label is re-sent and reaches the relay without user action.
Screenshots
n/a
Additional context
Root cause is in the Evolu client, not the relay: on a quota error the client only reports the error (@evolu/common Shared.ts, ApplySyncMessage error branch) and drops the message — there is no retry queue. Each mutation is pushed once with only its own delta; previously rejected changes are re-sent only on a full re-sync, which happens when the owner is (re)subscribed. Suite subscribes the owner exactly once per wallet (createEnsureStorage returns the cached storage afterwards) and nothing triggers a re-sync after the top-up in createSuiteSyncInternalErrorHandler.
Suggested fix (Suite-side, no library change): after a successful allocateOwnerQuota in the RelayQuotaExceeded branch, re-subscribe the owner — the same path reconnectAllRelays / evoluStorage.updateRelayUrl uses. The top-up e2e test then becomes the regression guard.
Related: #32070 (tests), branch quota-dead-sync-repro (standalone reproduction).
Describe the bug
When a wallet has used up its Suite Sync storage limit and the user edits a label, the relay rejects the write (
ProtocolQuotaError). Suite reacts correctly and tops the wallet's limit up from the device pool — but the rejected label is never sent again. It stays local only, with no error shown, until the relay connection is re-established (app restart, or re-saving the relay URL in debug settings). Labels edited after the top-up sync normally, so the user silently loses exactly the one edit that hit the limit.Info:
Steps to reproduce
Realistic:
Fast, deterministic (e2e infra on
develop, #32070):yarn workspace @trezor/suite-e2e docker:suite-syncsuite/e2e/tests/metadata/suite-sync/quota-manager.test.ts— the top-up test shrinks the wallet limit in the quota DB, writes a label, verifies the top-up happened, and fails on its last step because the label never reaches the relay.Without Suite (bare Evolu client, proves it is client library behavior): branch
quota-dead-sync-repro, then8 self-verifying checks, exit code 0 when all pass.
Expected behavior
After a successful quota top-up, the rejected label is re-sent and reaches the relay without user action.
Screenshots
n/a
Additional context
Root cause is in the Evolu client, not the relay: on a quota error the client only reports the error (
@evolu/commonShared.ts,ApplySyncMessageerror branch) and drops the message — there is no retry queue. Each mutation is pushed once with only its own delta; previously rejected changes are re-sent only on a full re-sync, which happens when the owner is (re)subscribed. Suite subscribes the owner exactly once per wallet (createEnsureStoragereturns the cached storage afterwards) and nothing triggers a re-sync after the top-up increateSuiteSyncInternalErrorHandler.Suggested fix (Suite-side, no library change): after a successful
allocateOwnerQuotain theRelayQuotaExceededbranch, re-subscribe the owner — the same pathreconnectAllRelays/evoluStorage.updateRelayUrluses. The top-up e2e test then becomes the regression guard.Related: #32070 (tests), branch
quota-dead-sync-repro(standalone reproduction).