Repository navigation
[Bug]: Renderer stops reporting client activity after an interrupted report, so usage limits never refresh again #15734
Description
Before submitting
- I searched existing issues and did not find a duplicate.
- I included enough detail to reproduce or investigate the problem.
Area
apps/web
Steps to reproduce
- Run the desktop app on macOS with the default
balancedbackground activity profile. - Close the lid so the Mac sleeps and dark-wakes for a while, so the renderer's environment connection is torn down while a
server.reportClientActivityrequest is in flight. - Open the lid and keep using the app.
Deterministic repro as a unit test: in backgroundActivityReporterLayer, make the first registry.run(...) for the activity report end with Effect.failCause(Cause.interrupt()), then advance the clock 60 s. No further reports are sent. The same test with Effect.fail(...) keeps reporting.
Expected behavior
The renderer keeps sending reportClientActivity every 25 s, so provider-status leases stay active and provider health and usage limits keep refreshing every 5 minutes.
Actual behavior
The renderer never reports client activity again until the window is reloaded. With no leases, BackgroundPolicy.shouldRunScopeWork({ type: "provider-status" }) is always false, so the provider refresh loop skips every interval. Usage limits froze for 4+ hours while the server kept running. All ~/.t3/caches/*.json usageLimits.checkedAt stopped at the same instant, and interacting with the window did not help. A manual server.refreshProviders call still worked immediately.
Cause: in apps/web/src/lib/backgroundActivityReporter.ts, each per-environment report is wrapped in Effect.ignore. That swallows failures but not interruption. An interrupted request ends report, which ends the Stream.runForEach(() => report) fiber. After that, the 25 s schedule and the interaction listeners keep offering to the sliding queue, but nothing consumes it.
Impact
Major degradation or frequent failure
Version or commit
T3 Code (Alpha) 0.0.45 desktop (official release). The same code is on main at 4ee6bfd.
Environment
macOS (Apple silicon), desktop app, default balanced background profile, several saved environments.
Logs or stack traces
From ~/.t3/userdata/logs/server.trace.ndjson* (local time):
syncProviderran every ~5 min, the last time at 22:31:49, then never again.web.backgroundActivity.reportran every ~25 s until 22:38:28. That last report took 15.1 s and ended withstatus.interrupted: true(⚠︎ Interrupted) during a lid-closed dark wake. There are zeroweb.backgroundActivity.report/server.reportClientActivityspans after it, through 4+ hours of active use.- Other 5-minute server work (
SourceControlProviderRegistry.detectProviderContext) continued normally the whole time.
Workaround
Reload the window (Cmd+R), or call server.refreshProviders manually.
I have a two-line fix with tests ready: Effect.ignore → Effect.ignoreCause for the per-environment report, plus a reporter-layer test covering both a failed and an interrupted report. The interrupted case fails without the fix. Happy to open a PR if this direction looks right.
Activity
juliusmarminge commentedon Oct 4, 2026
Note
Grok responding on behalf of Julius.
Triage
Thanks for the excellent write-up, @skyhong2002! The trace timings and the deterministic repro made this easy to confirm.
What I found
main(4ee6bfd5). This isn't a duplicate of the open usage-limit issues. Those cover reading or displaying limits; here the renderer stops asking for refreshes at all.Effect.ignoreonly swallows causes that contain aFail, and a disconnect doesn't produce one. Here's the sequence on wake:application-active, and the connection supervisor probes the live session for up to 15 s (CONNECTION_PROBE_TIMEOUT). That matches the 15.1 sweb.backgroundActivity.reportspan.apps/web/src/lib/backgroundActivityReporter.ts,Effect.ignore(around line 209) re-raises that interrupt. The per-environmentforEachthen fails and ends theStream.runForEach(() => report)consumer.forkScopeddoesn't restart that consumer, so the 25 s schedule and the interaction listeners keep offering to a queue that nothing drains.shouldRunScopeWork({ type: "provider-status" })stays false, so the provider refresh loop keeps skipping its work.server.refreshProvidersstill works because it doesn't check that lease.Likely fix area
Effect.ignoreCausefor the per-environmentregistry.run, so an interrupted report doesn't end the consumer.Effect.ignore({ log: true })still re-raises interrupt-only causes, so it wouldn't help.registry.runwithEffect.failCause(Cause.interrupt()), advance past one interval, and assert that a later report still runs.A maintainer will decide on the fix direction.