Summary
omni-dev gmail sync-all can silently drop an account's per-account summary line (and its progress bars) from terminal output, even though that account's sync completed successfully and its counts are correctly folded into the final combined: total. This looks like data loss but is purely a terminal-rendering race — no actual sync/data issue.
Repro
Run gmail sync-all across 5+ configured accounts (concurrency > 1) where multiple accounts finish sync around the same time. Example observed output (account names anonymized to match a 5-account config):
jky.greens: 2 fetched, 0 errors
777.punchbowl: 1 fetched, 2 labels updated, 0 errors
chateau.de.ky: 3 fetched, 1 labels updated, 0 errors
newhoggy: 348 fetched, 3 vanished, 4 labels updated, 0 errors
newhoggy ######## 351/351 messages fetched
chateau.de.ky ######## 3/3 messages fetched
combined: 361 fetched, 3 vanished, 29 labels updated, 0 errors
Only 4 of the 5 configured accounts (jky.greens, 777.punchbowl, chateau.de.ky, newhoggy) got a printed summary line; the 5th (prosperacommercial) never printed a line or progress bar at all. However:
- Sum of the 4 printed accounts: 354 fetched, 7 labels updated
combined: total: 361 fetched, 29 labels updated
- Difference (361-354=7 fetched, 29-7=22 labels updated) exactly matches a plausible contribution from the missing 5th account
So prosperacommercial synced successfully and its counts reached combined, but its own terminal output was lost.
Root cause
Two independent tasks both redraw the same shared indicatif::MultiProgress for a given account, with no ordering guarantee between them:
- The main loop prints that account's summary line via
multi.suspend(...) when its sync task completes (src/cli/gmail/sync_all.rs:316).
- A separate per-account render task (
src/cli/gmail/sync_all.rs:282, render_tasks.push(tokio::spawn(bars.drain(rx)))) calls finish_and_clear() / finish() on that account's bars once its event channel closes (src/cli/gmail/sync/progress.rs:113-114).
Both are triggered by the same underlying event — run_one_account returning, which drops progress_tx and closes the channel (src/cli/gmail/sync_all.rs:294-296) — but they run as two independent Tokio tasks with no join point between them. MultiProgress::suspend clears all bars, runs its closure, then redraws, but has no way to coordinate with a concurrent finish()/finish_and_clear() call from the render task. Whichever redraw wins the race can clobber output the other just wrote.
The code only awaits all render_tasks (guaranteeing bars are settled) before printing the final combined: total (see the comment around src/cli/gmail/sync_all.rs:323-328) — there's no equivalent guarantee before each individual per-account summary print during the loop. That gap is what lets an account's own line/bars get silently dropped while its data still reaches combined correctly (the outcomes used for combined are populated independently of the printing).
Suggested fix directions
- Await/join a given account's render task before (or as part of) printing its summary line, so the two redraws for that account can't race.
- Alternatively, serialize all prints and bar-finish calls through a single coordinating point (e.g. have the render task itself perform or trigger the summary-line print for its account after
finish_and_clear(), rather than having the main loop do it independently).
Impact
Cosmetic/observability only — no data loss. But it's misleading: a user watching the output can reasonably conclude an account was skipped entirely, when it actually synced fine.
Environment: omni-dev 0.41.0 (b37b0aed 2026-08-25), macOS, gmail-sync.yaml concurrency: 20, 5 configured accounts.
Summary
omni-dev gmail sync-allcan silently drop an account's per-account summary line (and its progress bars) from terminal output, even though that account's sync completed successfully and its counts are correctly folded into the finalcombined:total. This looks like data loss but is purely a terminal-rendering race — no actual sync/data issue.Repro
Run
gmail sync-allacross 5+ configured accounts (concurrency > 1) where multiple accounts finish sync around the same time. Example observed output (account names anonymized to match a 5-account config):Only 4 of the 5 configured accounts (
jky.greens,777.punchbowl,chateau.de.ky,newhoggy) got a printed summary line; the 5th (prosperacommercial) never printed a line or progress bar at all. However:combined:total: 361 fetched, 29 labels updatedSo
prosperacommercialsynced successfully and its counts reachedcombined, but its own terminal output was lost.Root cause
Two independent tasks both redraw the same shared
indicatif::MultiProgressfor a given account, with no ordering guarantee between them:multi.suspend(...)when its sync task completes (src/cli/gmail/sync_all.rs:316).src/cli/gmail/sync_all.rs:282,render_tasks.push(tokio::spawn(bars.drain(rx)))) callsfinish_and_clear()/finish()on that account's bars once its event channel closes (src/cli/gmail/sync/progress.rs:113-114).Both are triggered by the same underlying event —
run_one_accountreturning, which dropsprogress_txand closes the channel (src/cli/gmail/sync_all.rs:294-296) — but they run as two independent Tokio tasks with no join point between them.MultiProgress::suspendclears all bars, runs its closure, then redraws, but has no way to coordinate with a concurrentfinish()/finish_and_clear()call from the render task. Whichever redraw wins the race can clobber output the other just wrote.The code only awaits all
render_tasks(guaranteeing bars are settled) before printing the finalcombined:total (see the comment aroundsrc/cli/gmail/sync_all.rs:323-328) — there's no equivalent guarantee before each individual per-account summary print during the loop. That gap is what lets an account's own line/bars get silently dropped while its data still reachescombinedcorrectly (theoutcomesused forcombinedare populated independently of the printing).Suggested fix directions
finish_and_clear(), rather than having the main loop do it independently).Impact
Cosmetic/observability only — no data loss. But it's misleading: a user watching the output can reasonably conclude an account was skipped entirely, when it actually synced fine.
Environment:
omni-dev 0.41.0 (b37b0aed 2026-08-25), macOS,gmail-sync.yamlconcurrency: 20, 5 configured accounts.