Skip to content

[Bug]: Renderer stops reporting client activity after an interrupted report, so usage limits never refresh again #15734

Closed

Description

@skyhong2002

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

  1. Run the desktop app on macOS with the default balanced background activity profile.
  2. 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.reportClientActivity request is in flight.
  3. 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):

  • syncProvider ran every ~5 min, the last time at 22:31:49, then never again.
  • web.backgroundActivity.report ran every ~25 s until 22:38:28. That last report took 15.1 s and ended with status.interrupted: true (⚠︎ Interrupted) during a lid-closed dark wake. There are zero web.backgroundActivity.report / server.reportClientActivity spans 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

  1. juliusmarminge commented on Oct 4, 2026

    @juliusmarminge
    Member

    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

    • Confirmed on 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.
    • Why the report gets interrupted. Effect.ignore only swallows causes that contain a Fail, and a disconnect doesn't produce one. Here's the sequence on wake:
      • Opening the lid fires application-active, and the connection supervisor probes the live session for up to 15 s (CONNECTION_PROBE_TIMEOUT). That matches the 15.1 s web.backgroundActivity.report span.
      • When the probe times out, the session scope closes, and the RPC client finalizer resumes the in-flight call with an interrupt-only cause.
    • How that stops all later reports. In apps/web/src/lib/backgroundActivityReporter.ts, Effect.ignore (around line 209) re-raises that interrupt. The per-environment forEach then fails and ends the Stream.runForEach(() => report) consumer.
      • forkScoped doesn't restart that consumer, so the 25 s schedule and the interaction listeners keep offering to a queue that nothing drains.
      • After the 45 s lease expires, shouldRunScopeWork({ type: "provider-status" }) stays false, so the provider refresh loop keeps skipping its work.
      • server.refreshProviders still works because it doesn't check that lease.

    Likely fix area

    • Option 1: the change you proposed. Use Effect.ignoreCause for the per-environment registry.run, so an interrupted report doesn't end the consumer.
      • Note that Effect.ignore({ log: true }) still re-raises interrupt-only causes, so it wouldn't help.
      • Interruption is expected whenever a dead socket is replaced, so logging it would fire on every ordinary sleep and wake.
    • Option 2: a supervised consumer. Make the report consumer resilient on its own, so any unexpected end restarts it.
    • Regression test. Whichever direction is chosen, a reporter-layer test should cover it: fail the first registry.run with Effect.failCause(Cause.interrupt()), advance past one interval, and assert that a later report still runs.

    A maintainer will decide on the fix direction.

  2. added
    bugSomething is broken or behaving incorrectly.
    via-triageFiled through npx t3 triage
    on Oct 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions