[Bug]: Codex provider drains plan credits while T3 Code is idle in background (Codex equivalent of #2191) #2720

Description

@guilhermelippert

Before submitting

Area

apps/server

Summary

T3 Code (Alpha) drains the user's Codex Pro plan credits while idle in the background, even with the UI closed. In my case it burned 1000 purchased credits between ~01:00 BR and ~12:00 BR on 2026-05-15 while the app was minimized and the laptop was unattended. Activity pauses when the macOS lid is closed (system sleep) and resumes on wake — consistent with an in-app loop, not a server-side schedule.

The pattern matches the now-closed Claude bug #2191 ("Nightly: Claude Code burns tokens every 5 minutes while t3code is running idle"), but for the Codex provider. The fix Julius shipped for Claude in probeClaudeCapabilities() does not appear to extend to probeCodexAppServerProvider() in CodexProvider.ts.

Steps to reproduce

  1. Have Codex Pro authenticated locally (codex login, populates ~/.codex/auth.json).
  2. Launch T3 Code (Alpha), use it briefly, then minimize / close the window but leave the app running in the dock.
  3. Watch ~/.codex/logs_2.sqlite:
    SELECT datetime(ts,'unixepoch','-3 hours') AS brt, COUNT(*) AS events
    FROM logs
    WHERE feedback_log_body LIKE'%t3code_desktop%'GROUP BY strftime('%Y-%m-%d %H', datetime(ts,'unixepoch','-3 hours'))
    ORDER BY brt;
  4. Observe periodic model/list, account/rateLimits/read, and (less frequently but most expensive) responses_websocket activity with no corresponding user input. Killing every T3 process makes the t3code_desktop client_name disappear from the logs entirely.

Expected behavior

When the user has not issued a turn, T3 Code should not initiate Codex API calls — especially anything that hits the responses_websocket / sse::responses streaming endpoints, which incur per-token plan consumption.

Actual behavior

While idle, T3 Code keeps a Codex app-server process alive identifying itself as clientInfo.name = "t3code_desktop" (set in apps/server/src/provider/Layers/CodexProvider.ts lines 241 and 280), and periodically refreshes its provider snapshot via makeManagedServerProvider.ts lines 141–146 — an unconditional Effect.forever(Effect.sleep(refreshInterval)) loop. CodexDriver.ts sets SNAPSHOT_REFRESH_INTERVAL = Duration.minutes(5).

The refresh itself calls probeCodexAppServerProvider which sends initialize, account/read, model/list, and skills/list. Those are not the costly ones on their own — but they keep the websocket warm and, in my observed window, also produced bursts of codex_api::endpoint::responses_websocket activity (e.g. 279 events in a single minute at 08:28 BR) while I was asleep. I don't have a smoking-gun trace of which T3 code path triggers those — that's where I need the maintainers to look. The strongest candidates:

  • A stuck activeTurnId keeping ProviderSessionReaper from reaping the orphan session (ProviderSessionReaper.ts:64-71 short-circuits when activeTurnId != null). This is what PR reconcile provider session state and settle stuck turns #2666 already addresses.
  • The Codex websocket warmup running on every refresh / reconnect.

Why the Claude fix doesn't cover this

Issue #2191 was closed because probeClaudeCapabilities() was changed to stop hitting the Anthropic API. The Codex equivalent (probeCodexAppServerProvider in apps/server/src/provider/Layers/CodexProvider.ts) was not given the same treatment — it still spawns a Codex app-server every refresh, which in turn handshakes against chatgpt.com. And the broader gating PR #2679 ("Add background activity policy and host power monitoring") is still OPEN, CONFLICTING, not merged — so even released builds remain affected.

Evidence

1. Burn timeline (Brazil time, UTC-3) — from ~/.codex/logs_2.sqlite

The last user-initiated Codex rollout file in ~/.codex/sessions/ is 2026-05-14T22:32 BR (<turn_aborted> as final event). Everything after this is non-user-initiated.

Hora BREventsNotes
14/05 22:5012,087tail of user session
15/05 01:4192first t3code_desktop calls to chatgpt.com (model/list)
15/05 03:32t3code_desktop calls account/rateLimits/read
15/05 03:48699t3code_desktop heavy refresh
15/05 08:28279 events on codex_api::endpoint::responses_websocket + 19 on codex_api::sse::responses (asleep, no input)
15/05 10:571,301sustained

2. t3code_desktop is the only non-Codex-official client_name seen

Distinct client_name values observed making outbound calls during the burn window:

  • t3code_desktop — the only third-party identifier
  • codex_desktop / codex_cli — official OpenAI Codex clients (user-driven during earlier daytime hours)

3. Burn stops the moment T3 is killed

After pkill -f "T3 Code" and trashing the app:

  • 0 t3code_desktop log entries in the next 5 minutes.
  • Subsequent Codex API attempts (from official Codex clients reconnecting) immediately get error.message="You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at May 18th, 2026 7:50 PM." — confirming the plan quota was the constrained resource.

4. Process tree right before the kill

PID ELAPSED COMMAND
36707 05:34:40 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)
36717 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=gpu-process --user-data-dir=.../t3code
36718 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=utility --utility-sub-type=network.mojom.NetworkService
36783 05:34:37 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha) /Applications/T3 Code (Alpha).app/Contents/Resources/app.asar/apps/server/dist/bin.mjs --bootstrap-fd 3
37178 05:34:30 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper (Renderer)

apps/server/dist/bin.mjs (pid 36783) is the in-app server holding the Codex app-server channels open.

Suggested places to look

  1. apps/server/src/provider/Layers/CodexProvider.ts:251probeCodexAppServerProvider. Gate this behind foreground client demand the same way Reduce idle work and disk churn with native resource diagnostics #2679 plans to gate makeManagedServerProvider, or change it to read account/model info from local Codex state instead of spawning a fresh app-server every 5 minutes.
  2. apps/server/src/provider/Layers/ProviderSessionReaper.ts:64-71 — the activeTurnId != null short-circuit. PR reconcile provider session state and settle stuck turns #2666 addresses this; a stuck activeTurnId after <turn_aborted> means the reaper never tears down orphan sessions, and any background reconnect/warmup on that session is on the user's dime.
  3. Whatever path triggers codex_api::endpoint::responses_websocket traffic from a non-user-initiated context. Most likely candidates: websocket warmup running on every refresh tick, or a stuck turn being silently retried.

Impact

Blocks work completely — drained 1000 purchased credits in ~14 hours of idle time. The user is now blocked from Codex use until 2026-05-18 19:50 BR (next plan window reset).

Version or commit

T3 Code (Alpha) — latest installer as of 2026-05-15 (the package.json in main reads 0.0.24).
Codex CLI spawned by T3: 0.131.0-alpha.9.

Environment

macOS Darwin 25.3.0 (Apple Silicon), ~/.codex auth via OAuth (auth_mode = "Chatgpt", Codex Pro plan).

Related

Happy to share raw logs_2.sqlite excerpts or specific timestamps on request.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
       blocks
      (function() {
      function addCopyButtons() {
      document.querySelectorAll('pre code').forEach(function(codeBlock) {
      if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
      codeBlock.parentElement.setAttribute('data-copy-added', 'true');
      var btn = document.createElement('button');
      btn.textContent = 'Copy';
      btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
      btn.onmouseover = function() { this.style.opacity = '1'; };
      btn.onmouseout = function() { this.style.opacity = '0.7'; };
      btn.onclick = function() {
      navigator.clipboard.writeText(codeBlock.textContent).then(function() {
      btn.textContent = 'Copied!';
      setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
      });
      };
      codeBlock.parentElement.style.position = 'relative';
      codeBlock.parentElement.appendChild(btn);
      });
      }
      addCopyButtons();
      // Re-run on dynamic content
      var observer = new MutationObserver(addCopyButtons);
      observer.observe(document.body, { childList: true, subtree: true });
      })();
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      [Bug]: Codex provider drains plan credits while T3 Code is idle in background (Codex equivalent of #2191) #2720

      Description

      @guilhermelippert

      Before submitting

      Area

      apps/server

      Summary

      T3 Code (Alpha) drains the user's Codex Pro plan credits while idle in the background, even with the UI closed. In my case it burned 1000 purchased credits between ~01:00 BR and ~12:00 BR on 2026-05-15 while the app was minimized and the laptop was unattended. Activity pauses when the macOS lid is closed (system sleep) and resumes on wake — consistent with an in-app loop, not a server-side schedule.

      The pattern matches the now-closed Claude bug #2191 ("Nightly: Claude Code burns tokens every 5 minutes while t3code is running idle"), but for the Codex provider. The fix Julius shipped for Claude in probeClaudeCapabilities() does not appear to extend to probeCodexAppServerProvider() in CodexProvider.ts.

      Steps to reproduce

      1. Have Codex Pro authenticated locally (codex login, populates ~/.codex/auth.json).
      2. Launch T3 Code (Alpha), use it briefly, then minimize / close the window but leave the app running in the dock.
      3. Watch ~/.codex/logs_2.sqlite:
        SELECT datetime(ts,'unixepoch','-3 hours') AS brt, COUNT(*) AS events
        FROM logs
        WHERE feedback_log_body LIKE'%t3code_desktop%'GROUP BY strftime('%Y-%m-%d %H', datetime(ts,'unixepoch','-3 hours'))
        ORDER BY brt;
      4. Observe periodic model/list, account/rateLimits/read, and (less frequently but most expensive) responses_websocket activity with no corresponding user input. Killing every T3 process makes the t3code_desktop client_name disappear from the logs entirely.

      Expected behavior

      When the user has not issued a turn, T3 Code should not initiate Codex API calls — especially anything that hits the responses_websocket / sse::responses streaming endpoints, which incur per-token plan consumption.

      Actual behavior

      While idle, T3 Code keeps a Codex app-server process alive identifying itself as clientInfo.name = "t3code_desktop" (set in apps/server/src/provider/Layers/CodexProvider.ts lines 241 and 280), and periodically refreshes its provider snapshot via makeManagedServerProvider.ts lines 141–146 — an unconditional Effect.forever(Effect.sleep(refreshInterval)) loop. CodexDriver.ts sets SNAPSHOT_REFRESH_INTERVAL = Duration.minutes(5).

      The refresh itself calls probeCodexAppServerProvider which sends initialize, account/read, model/list, and skills/list. Those are not the costly ones on their own — but they keep the websocket warm and, in my observed window, also produced bursts of codex_api::endpoint::responses_websocket activity (e.g. 279 events in a single minute at 08:28 BR) while I was asleep. I don't have a smoking-gun trace of which T3 code path triggers those — that's where I need the maintainers to look. The strongest candidates:

      • A stuck activeTurnId keeping ProviderSessionReaper from reaping the orphan session (ProviderSessionReaper.ts:64-71 short-circuits when activeTurnId != null). This is what PR reconcile provider session state and settle stuck turns #2666 already addresses.
      • The Codex websocket warmup running on every refresh / reconnect.

      Why the Claude fix doesn't cover this

      Issue #2191 was closed because probeClaudeCapabilities() was changed to stop hitting the Anthropic API. The Codex equivalent (probeCodexAppServerProvider in apps/server/src/provider/Layers/CodexProvider.ts) was not given the same treatment — it still spawns a Codex app-server every refresh, which in turn handshakes against chatgpt.com. And the broader gating PR #2679 ("Add background activity policy and host power monitoring") is still OPEN, CONFLICTING, not merged — so even released builds remain affected.

      Evidence

      1. Burn timeline (Brazil time, UTC-3) — from ~/.codex/logs_2.sqlite

      The last user-initiated Codex rollout file in ~/.codex/sessions/ is 2026-05-14T22:32 BR (<turn_aborted> as final event). Everything after this is non-user-initiated.

      Hora BREventsNotes
      14/05 22:5012,087tail of user session
      15/05 01:4192first t3code_desktop calls to chatgpt.com (model/list)
      15/05 03:32t3code_desktop calls account/rateLimits/read
      15/05 03:48699t3code_desktop heavy refresh
      15/05 08:28279 events on codex_api::endpoint::responses_websocket + 19 on codex_api::sse::responses (asleep, no input)
      15/05 10:571,301sustained

      2. t3code_desktop is the only non-Codex-official client_name seen

      Distinct client_name values observed making outbound calls during the burn window:

      • t3code_desktop — the only third-party identifier
      • codex_desktop / codex_cli — official OpenAI Codex clients (user-driven during earlier daytime hours)

      3. Burn stops the moment T3 is killed

      After pkill -f "T3 Code" and trashing the app:

      • 0 t3code_desktop log entries in the next 5 minutes.
      • Subsequent Codex API attempts (from official Codex clients reconnecting) immediately get error.message="You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at May 18th, 2026 7:50 PM." — confirming the plan quota was the constrained resource.

      4. Process tree right before the kill

      PID ELAPSED COMMAND
      36707 05:34:40 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)
      36717 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=gpu-process --user-data-dir=.../t3code
      36718 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=utility --utility-sub-type=network.mojom.NetworkService
      36783 05:34:37 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha) /Applications/T3 Code (Alpha).app/Contents/Resources/app.asar/apps/server/dist/bin.mjs --bootstrap-fd 3
      37178 05:34:30 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper (Renderer)
      

      apps/server/dist/bin.mjs (pid 36783) is the in-app server holding the Codex app-server channels open.

      Suggested places to look

      1. apps/server/src/provider/Layers/CodexProvider.ts:251probeCodexAppServerProvider. Gate this behind foreground client demand the same way Reduce idle work and disk churn with native resource diagnostics #2679 plans to gate makeManagedServerProvider, or change it to read account/model info from local Codex state instead of spawning a fresh app-server every 5 minutes.
      2. apps/server/src/provider/Layers/ProviderSessionReaper.ts:64-71 — the activeTurnId != null short-circuit. PR reconcile provider session state and settle stuck turns #2666 addresses this; a stuck activeTurnId after <turn_aborted> means the reaper never tears down orphan sessions, and any background reconnect/warmup on that session is on the user's dime.
      3. Whatever path triggers codex_api::endpoint::responses_websocket traffic from a non-user-initiated context. Most likely candidates: websocket warmup running on every refresh tick, or a stuck turn being silently retried.

      Impact

      Blocks work completely — drained 1000 purchased credits in ~14 hours of idle time. The user is now blocked from Codex use until 2026-05-18 19:50 BR (next plan window reset).

      Version or commit

      T3 Code (Alpha) — latest installer as of 2026-05-15 (the package.json in main reads 0.0.24).
      Codex CLI spawned by T3: 0.131.0-alpha.9.

      Environment

      macOS Darwin 25.3.0 (Apple Silicon), ~/.codex auth via OAuth (auth_mode = "Chatgpt", Codex Pro plan).

      Related

      Happy to share raw logs_2.sqlite excerpts or specific timestamps on request.

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        No labels
        No labels

        Type

        No type

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          [Bug]: Codex provider drains plan credits while T3 Code is idle in background (Codex equivalent of #2191) #2720

          Description

          @guilhermelippert

          Before submitting

          Area

          apps/server

          Summary

          T3 Code (Alpha) drains the user's Codex Pro plan credits while idle in the background, even with the UI closed. In my case it burned 1000 purchased credits between ~01:00 BR and ~12:00 BR on 2026-05-15 while the app was minimized and the laptop was unattended. Activity pauses when the macOS lid is closed (system sleep) and resumes on wake — consistent with an in-app loop, not a server-side schedule.

          The pattern matches the now-closed Claude bug #2191 ("Nightly: Claude Code burns tokens every 5 minutes while t3code is running idle"), but for the Codex provider. The fix Julius shipped for Claude in probeClaudeCapabilities() does not appear to extend to probeCodexAppServerProvider() in CodexProvider.ts.

          Steps to reproduce

          1. Have Codex Pro authenticated locally (codex login, populates ~/.codex/auth.json).
          2. Launch T3 Code (Alpha), use it briefly, then minimize / close the window but leave the app running in the dock.
          3. Watch ~/.codex/logs_2.sqlite:
            SELECT datetime(ts,'unixepoch','-3 hours') AS brt, COUNT(*) AS events
            FROM logs
            WHERE feedback_log_body LIKE'%t3code_desktop%'GROUP BY strftime('%Y-%m-%d %H', datetime(ts,'unixepoch','-3 hours'))
            ORDER BY brt;
          4. Observe periodic model/list, account/rateLimits/read, and (less frequently but most expensive) responses_websocket activity with no corresponding user input. Killing every T3 process makes the t3code_desktop client_name disappear from the logs entirely.

          Expected behavior

          When the user has not issued a turn, T3 Code should not initiate Codex API calls — especially anything that hits the responses_websocket / sse::responses streaming endpoints, which incur per-token plan consumption.

          Actual behavior

          While idle, T3 Code keeps a Codex app-server process alive identifying itself as clientInfo.name = "t3code_desktop" (set in apps/server/src/provider/Layers/CodexProvider.ts lines 241 and 280), and periodically refreshes its provider snapshot via makeManagedServerProvider.ts lines 141–146 — an unconditional Effect.forever(Effect.sleep(refreshInterval)) loop. CodexDriver.ts sets SNAPSHOT_REFRESH_INTERVAL = Duration.minutes(5).

          The refresh itself calls probeCodexAppServerProvider which sends initialize, account/read, model/list, and skills/list. Those are not the costly ones on their own — but they keep the websocket warm and, in my observed window, also produced bursts of codex_api::endpoint::responses_websocket activity (e.g. 279 events in a single minute at 08:28 BR) while I was asleep. I don't have a smoking-gun trace of which T3 code path triggers those — that's where I need the maintainers to look. The strongest candidates:

          • A stuck activeTurnId keeping ProviderSessionReaper from reaping the orphan session (ProviderSessionReaper.ts:64-71 short-circuits when activeTurnId != null). This is what PR reconcile provider session state and settle stuck turns #2666 already addresses.
          • The Codex websocket warmup running on every refresh / reconnect.

          Why the Claude fix doesn't cover this

          Issue #2191 was closed because probeClaudeCapabilities() was changed to stop hitting the Anthropic API. The Codex equivalent (probeCodexAppServerProvider in apps/server/src/provider/Layers/CodexProvider.ts) was not given the same treatment — it still spawns a Codex app-server every refresh, which in turn handshakes against chatgpt.com. And the broader gating PR #2679 ("Add background activity policy and host power monitoring") is still OPEN, CONFLICTING, not merged — so even released builds remain affected.

          Evidence

          1. Burn timeline (Brazil time, UTC-3) — from ~/.codex/logs_2.sqlite

          The last user-initiated Codex rollout file in ~/.codex/sessions/ is 2026-05-14T22:32 BR (<turn_aborted> as final event). Everything after this is non-user-initiated.

          Hora BREventsNotes
          14/05 22:5012,087tail of user session
          15/05 01:4192first t3code_desktop calls to chatgpt.com (model/list)
          15/05 03:32t3code_desktop calls account/rateLimits/read
          15/05 03:48699t3code_desktop heavy refresh
          15/05 08:28279 events on codex_api::endpoint::responses_websocket + 19 on codex_api::sse::responses (asleep, no input)
          15/05 10:571,301sustained

          2. t3code_desktop is the only non-Codex-official client_name seen

          Distinct client_name values observed making outbound calls during the burn window:

          • t3code_desktop — the only third-party identifier
          • codex_desktop / codex_cli — official OpenAI Codex clients (user-driven during earlier daytime hours)

          3. Burn stops the moment T3 is killed

          After pkill -f "T3 Code" and trashing the app:

          • 0 t3code_desktop log entries in the next 5 minutes.
          • Subsequent Codex API attempts (from official Codex clients reconnecting) immediately get error.message="You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at May 18th, 2026 7:50 PM." — confirming the plan quota was the constrained resource.

          4. Process tree right before the kill

          PID ELAPSED COMMAND
          36707 05:34:40 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)
          36717 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=gpu-process --user-data-dir=.../t3code
          36718 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=utility --utility-sub-type=network.mojom.NetworkService
          36783 05:34:37 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha) /Applications/T3 Code (Alpha).app/Contents/Resources/app.asar/apps/server/dist/bin.mjs --bootstrap-fd 3
          37178 05:34:30 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper (Renderer)
          

          apps/server/dist/bin.mjs (pid 36783) is the in-app server holding the Codex app-server channels open.

          Suggested places to look

          1. apps/server/src/provider/Layers/CodexProvider.ts:251probeCodexAppServerProvider. Gate this behind foreground client demand the same way Reduce idle work and disk churn with native resource diagnostics #2679 plans to gate makeManagedServerProvider, or change it to read account/model info from local Codex state instead of spawning a fresh app-server every 5 minutes.
          2. apps/server/src/provider/Layers/ProviderSessionReaper.ts:64-71 — the activeTurnId != null short-circuit. PR reconcile provider session state and settle stuck turns #2666 addresses this; a stuck activeTurnId after <turn_aborted> means the reaper never tears down orphan sessions, and any background reconnect/warmup on that session is on the user's dime.
          3. Whatever path triggers codex_api::endpoint::responses_websocket traffic from a non-user-initiated context. Most likely candidates: websocket warmup running on every refresh tick, or a stuck turn being silently retried.

          Impact

          Blocks work completely — drained 1000 purchased credits in ~14 hours of idle time. The user is now blocked from Codex use until 2026-05-18 19:50 BR (next plan window reset).

          Version or commit

          T3 Code (Alpha) — latest installer as of 2026-05-15 (the package.json in main reads 0.0.24).
          Codex CLI spawned by T3: 0.131.0-alpha.9.

          Environment

          macOS Darwin 25.3.0 (Apple Silicon), ~/.codex auth via OAuth (auth_mode = "Chatgpt", Codex Pro plan).

          Related

          Happy to share raw logs_2.sqlite excerpts or specific timestamps on request.

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            No labels
            No labels

            Type

            No type

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              [Bug]: Codex provider drains plan credits while T3 Code is idle in background (Codex equivalent of #2191) #2720

              Description

              @guilhermelippert

              Before submitting

              Area

              apps/server

              Summary

              T3 Code (Alpha) drains the user's Codex Pro plan credits while idle in the background, even with the UI closed. In my case it burned 1000 purchased credits between ~01:00 BR and ~12:00 BR on 2026-05-15 while the app was minimized and the laptop was unattended. Activity pauses when the macOS lid is closed (system sleep) and resumes on wake — consistent with an in-app loop, not a server-side schedule.

              The pattern matches the now-closed Claude bug #2191 ("Nightly: Claude Code burns tokens every 5 minutes while t3code is running idle"), but for the Codex provider. The fix Julius shipped for Claude in probeClaudeCapabilities() does not appear to extend to probeCodexAppServerProvider() in CodexProvider.ts.

              Steps to reproduce

              1. Have Codex Pro authenticated locally (codex login, populates ~/.codex/auth.json).
              2. Launch T3 Code (Alpha), use it briefly, then minimize / close the window but leave the app running in the dock.
              3. Watch ~/.codex/logs_2.sqlite:
                SELECT datetime(ts,'unixepoch','-3 hours') AS brt, COUNT(*) AS events
                FROM logs
                WHERE feedback_log_body LIKE'%t3code_desktop%'GROUP BY strftime('%Y-%m-%d %H', datetime(ts,'unixepoch','-3 hours'))
                ORDER BY brt;
              4. Observe periodic model/list, account/rateLimits/read, and (less frequently but most expensive) responses_websocket activity with no corresponding user input. Killing every T3 process makes the t3code_desktop client_name disappear from the logs entirely.

              Expected behavior

              When the user has not issued a turn, T3 Code should not initiate Codex API calls — especially anything that hits the responses_websocket / sse::responses streaming endpoints, which incur per-token plan consumption.

              Actual behavior

              While idle, T3 Code keeps a Codex app-server process alive identifying itself as clientInfo.name = "t3code_desktop" (set in apps/server/src/provider/Layers/CodexProvider.ts lines 241 and 280), and periodically refreshes its provider snapshot via makeManagedServerProvider.ts lines 141–146 — an unconditional Effect.forever(Effect.sleep(refreshInterval)) loop. CodexDriver.ts sets SNAPSHOT_REFRESH_INTERVAL = Duration.minutes(5).

              The refresh itself calls probeCodexAppServerProvider which sends initialize, account/read, model/list, and skills/list. Those are not the costly ones on their own — but they keep the websocket warm and, in my observed window, also produced bursts of codex_api::endpoint::responses_websocket activity (e.g. 279 events in a single minute at 08:28 BR) while I was asleep. I don't have a smoking-gun trace of which T3 code path triggers those — that's where I need the maintainers to look. The strongest candidates:

              • A stuck activeTurnId keeping ProviderSessionReaper from reaping the orphan session (ProviderSessionReaper.ts:64-71 short-circuits when activeTurnId != null). This is what PR reconcile provider session state and settle stuck turns #2666 already addresses.
              • The Codex websocket warmup running on every refresh / reconnect.

              Why the Claude fix doesn't cover this

              Issue #2191 was closed because probeClaudeCapabilities() was changed to stop hitting the Anthropic API. The Codex equivalent (probeCodexAppServerProvider in apps/server/src/provider/Layers/CodexProvider.ts) was not given the same treatment — it still spawns a Codex app-server every refresh, which in turn handshakes against chatgpt.com. And the broader gating PR #2679 ("Add background activity policy and host power monitoring") is still OPEN, CONFLICTING, not merged — so even released builds remain affected.

              Evidence

              1. Burn timeline (Brazil time, UTC-3) — from ~/.codex/logs_2.sqlite

              The last user-initiated Codex rollout file in ~/.codex/sessions/ is 2026-05-14T22:32 BR (<turn_aborted> as final event). Everything after this is non-user-initiated.

              Hora BREventsNotes
              14/05 22:5012,087tail of user session
              15/05 01:4192first t3code_desktop calls to chatgpt.com (model/list)
              15/05 03:32t3code_desktop calls account/rateLimits/read
              15/05 03:48699t3code_desktop heavy refresh
              15/05 08:28279 events on codex_api::endpoint::responses_websocket + 19 on codex_api::sse::responses (asleep, no input)
              15/05 10:571,301sustained

              2. t3code_desktop is the only non-Codex-official client_name seen

              Distinct client_name values observed making outbound calls during the burn window:

              • t3code_desktop — the only third-party identifier
              • codex_desktop / codex_cli — official OpenAI Codex clients (user-driven during earlier daytime hours)

              3. Burn stops the moment T3 is killed

              After pkill -f "T3 Code" and trashing the app:

              • 0 t3code_desktop log entries in the next 5 minutes.
              • Subsequent Codex API attempts (from official Codex clients reconnecting) immediately get error.message="You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at May 18th, 2026 7:50 PM." — confirming the plan quota was the constrained resource.

              4. Process tree right before the kill

              PID ELAPSED COMMAND
              36707 05:34:40 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)
              36717 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=gpu-process --user-data-dir=.../t3code
              36718 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=utility --utility-sub-type=network.mojom.NetworkService
              36783 05:34:37 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha) /Applications/T3 Code (Alpha).app/Contents/Resources/app.asar/apps/server/dist/bin.mjs --bootstrap-fd 3
              37178 05:34:30 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper (Renderer)
              

              apps/server/dist/bin.mjs (pid 36783) is the in-app server holding the Codex app-server channels open.

              Suggested places to look

              1. apps/server/src/provider/Layers/CodexProvider.ts:251probeCodexAppServerProvider. Gate this behind foreground client demand the same way Reduce idle work and disk churn with native resource diagnostics #2679 plans to gate makeManagedServerProvider, or change it to read account/model info from local Codex state instead of spawning a fresh app-server every 5 minutes.
              2. apps/server/src/provider/Layers/ProviderSessionReaper.ts:64-71 — the activeTurnId != null short-circuit. PR reconcile provider session state and settle stuck turns #2666 addresses this; a stuck activeTurnId after <turn_aborted> means the reaper never tears down orphan sessions, and any background reconnect/warmup on that session is on the user's dime.
              3. Whatever path triggers codex_api::endpoint::responses_websocket traffic from a non-user-initiated context. Most likely candidates: websocket warmup running on every refresh tick, or a stuck turn being silently retried.

              Impact

              Blocks work completely — drained 1000 purchased credits in ~14 hours of idle time. The user is now blocked from Codex use until 2026-05-18 19:50 BR (next plan window reset).

              Version or commit

              T3 Code (Alpha) — latest installer as of 2026-05-15 (the package.json in main reads 0.0.24).
              Codex CLI spawned by T3: 0.131.0-alpha.9.

              Environment

              macOS Darwin 25.3.0 (Apple Silicon), ~/.codex auth via OAuth (auth_mode = "Chatgpt", Codex Pro plan).

              Related

              Happy to share raw logs_2.sqlite excerpts or specific timestamps on request.

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                No labels
                No labels

                Type

                No type

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  [Bug]: Codex provider drains plan credits while T3 Code is idle in background (Codex equivalent of #2191) #2720

                  Description

                  @guilhermelippert

                  Before submitting

                  Area

                  apps/server

                  Summary

                  T3 Code (Alpha) drains the user's Codex Pro plan credits while idle in the background, even with the UI closed. In my case it burned 1000 purchased credits between ~01:00 BR and ~12:00 BR on 2026-05-15 while the app was minimized and the laptop was unattended. Activity pauses when the macOS lid is closed (system sleep) and resumes on wake — consistent with an in-app loop, not a server-side schedule.

                  The pattern matches the now-closed Claude bug #2191 ("Nightly: Claude Code burns tokens every 5 minutes while t3code is running idle"), but for the Codex provider. The fix Julius shipped for Claude in probeClaudeCapabilities() does not appear to extend to probeCodexAppServerProvider() in CodexProvider.ts.

                  Steps to reproduce

                  1. Have Codex Pro authenticated locally (codex login, populates ~/.codex/auth.json).
                  2. Launch T3 Code (Alpha), use it briefly, then minimize / close the window but leave the app running in the dock.
                  3. Watch ~/.codex/logs_2.sqlite:
                    SELECT datetime(ts,'unixepoch','-3 hours') AS brt, COUNT(*) AS events
                    FROM logs
                    WHERE feedback_log_body LIKE'%t3code_desktop%'GROUP BY strftime('%Y-%m-%d %H', datetime(ts,'unixepoch','-3 hours'))
                    ORDER BY brt;
                  4. Observe periodic model/list, account/rateLimits/read, and (less frequently but most expensive) responses_websocket activity with no corresponding user input. Killing every T3 process makes the t3code_desktop client_name disappear from the logs entirely.

                  Expected behavior

                  When the user has not issued a turn, T3 Code should not initiate Codex API calls — especially anything that hits the responses_websocket / sse::responses streaming endpoints, which incur per-token plan consumption.

                  Actual behavior

                  While idle, T3 Code keeps a Codex app-server process alive identifying itself as clientInfo.name = "t3code_desktop" (set in apps/server/src/provider/Layers/CodexProvider.ts lines 241 and 280), and periodically refreshes its provider snapshot via makeManagedServerProvider.ts lines 141–146 — an unconditional Effect.forever(Effect.sleep(refreshInterval)) loop. CodexDriver.ts sets SNAPSHOT_REFRESH_INTERVAL = Duration.minutes(5).

                  The refresh itself calls probeCodexAppServerProvider which sends initialize, account/read, model/list, and skills/list. Those are not the costly ones on their own — but they keep the websocket warm and, in my observed window, also produced bursts of codex_api::endpoint::responses_websocket activity (e.g. 279 events in a single minute at 08:28 BR) while I was asleep. I don't have a smoking-gun trace of which T3 code path triggers those — that's where I need the maintainers to look. The strongest candidates:

                  • A stuck activeTurnId keeping ProviderSessionReaper from reaping the orphan session (ProviderSessionReaper.ts:64-71 short-circuits when activeTurnId != null). This is what PR reconcile provider session state and settle stuck turns #2666 already addresses.
                  • The Codex websocket warmup running on every refresh / reconnect.

                  Why the Claude fix doesn't cover this

                  Issue #2191 was closed because probeClaudeCapabilities() was changed to stop hitting the Anthropic API. The Codex equivalent (probeCodexAppServerProvider in apps/server/src/provider/Layers/CodexProvider.ts) was not given the same treatment — it still spawns a Codex app-server every refresh, which in turn handshakes against chatgpt.com. And the broader gating PR #2679 ("Add background activity policy and host power monitoring") is still OPEN, CONFLICTING, not merged — so even released builds remain affected.

                  Evidence

                  1. Burn timeline (Brazil time, UTC-3) — from ~/.codex/logs_2.sqlite

                  The last user-initiated Codex rollout file in ~/.codex/sessions/ is 2026-05-14T22:32 BR (<turn_aborted> as final event). Everything after this is non-user-initiated.

                  Hora BREventsNotes
                  14/05 22:5012,087tail of user session
                  15/05 01:4192first t3code_desktop calls to chatgpt.com (model/list)
                  15/05 03:32t3code_desktop calls account/rateLimits/read
                  15/05 03:48699t3code_desktop heavy refresh
                  15/05 08:28279 events on codex_api::endpoint::responses_websocket + 19 on codex_api::sse::responses (asleep, no input)
                  15/05 10:571,301sustained

                  2. t3code_desktop is the only non-Codex-official client_name seen

                  Distinct client_name values observed making outbound calls during the burn window:

                  • t3code_desktop — the only third-party identifier
                  • codex_desktop / codex_cli — official OpenAI Codex clients (user-driven during earlier daytime hours)

                  3. Burn stops the moment T3 is killed

                  After pkill -f "T3 Code" and trashing the app:

                  • 0 t3code_desktop log entries in the next 5 minutes.
                  • Subsequent Codex API attempts (from official Codex clients reconnecting) immediately get error.message="You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at May 18th, 2026 7:50 PM." — confirming the plan quota was the constrained resource.

                  4. Process tree right before the kill

                  PID ELAPSED COMMAND
                  36707 05:34:40 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)
                  36717 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=gpu-process --user-data-dir=.../t3code
                  36718 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=utility --utility-sub-type=network.mojom.NetworkService
                  36783 05:34:37 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha) /Applications/T3 Code (Alpha).app/Contents/Resources/app.asar/apps/server/dist/bin.mjs --bootstrap-fd 3
                  37178 05:34:30 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper (Renderer)
                  

                  apps/server/dist/bin.mjs (pid 36783) is the in-app server holding the Codex app-server channels open.

                  Suggested places to look

                  1. apps/server/src/provider/Layers/CodexProvider.ts:251probeCodexAppServerProvider. Gate this behind foreground client demand the same way Reduce idle work and disk churn with native resource diagnostics #2679 plans to gate makeManagedServerProvider, or change it to read account/model info from local Codex state instead of spawning a fresh app-server every 5 minutes.
                  2. apps/server/src/provider/Layers/ProviderSessionReaper.ts:64-71 — the activeTurnId != null short-circuit. PR reconcile provider session state and settle stuck turns #2666 addresses this; a stuck activeTurnId after <turn_aborted> means the reaper never tears down orphan sessions, and any background reconnect/warmup on that session is on the user's dime.
                  3. Whatever path triggers codex_api::endpoint::responses_websocket traffic from a non-user-initiated context. Most likely candidates: websocket warmup running on every refresh tick, or a stuck turn being silently retried.

                  Impact

                  Blocks work completely — drained 1000 purchased credits in ~14 hours of idle time. The user is now blocked from Codex use until 2026-05-18 19:50 BR (next plan window reset).

                  Version or commit

                  T3 Code (Alpha) — latest installer as of 2026-05-15 (the package.json in main reads 0.0.24).
                  Codex CLI spawned by T3: 0.131.0-alpha.9.

                  Environment

                  macOS Darwin 25.3.0 (Apple Silicon), ~/.codex auth via OAuth (auth_mode = "Chatgpt", Codex Pro plan).

                  Related

                  Happy to share raw logs_2.sqlite excerpts or specific timestamps on request.

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    No labels
                    No labels

                    Type

                    No type

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      [Bug]: Codex provider drains plan credits while T3 Code is idle in background (Codex equivalent of #2191) #2720

                      Description

                      @guilhermelippert

                      Before submitting

                      Area

                      apps/server

                      Summary

                      T3 Code (Alpha) drains the user's Codex Pro plan credits while idle in the background, even with the UI closed. In my case it burned 1000 purchased credits between ~01:00 BR and ~12:00 BR on 2026-05-15 while the app was minimized and the laptop was unattended. Activity pauses when the macOS lid is closed (system sleep) and resumes on wake — consistent with an in-app loop, not a server-side schedule.

                      The pattern matches the now-closed Claude bug #2191 ("Nightly: Claude Code burns tokens every 5 minutes while t3code is running idle"), but for the Codex provider. The fix Julius shipped for Claude in probeClaudeCapabilities() does not appear to extend to probeCodexAppServerProvider() in CodexProvider.ts.

                      Steps to reproduce

                      1. Have Codex Pro authenticated locally (codex login, populates ~/.codex/auth.json).
                      2. Launch T3 Code (Alpha), use it briefly, then minimize / close the window but leave the app running in the dock.
                      3. Watch ~/.codex/logs_2.sqlite:
                        SELECT datetime(ts,'unixepoch','-3 hours') AS brt, COUNT(*) AS events
                        FROM logs
                        WHERE feedback_log_body LIKE'%t3code_desktop%'GROUP BY strftime('%Y-%m-%d %H', datetime(ts,'unixepoch','-3 hours'))
                        ORDER BY brt;
                      4. Observe periodic model/list, account/rateLimits/read, and (less frequently but most expensive) responses_websocket activity with no corresponding user input. Killing every T3 process makes the t3code_desktop client_name disappear from the logs entirely.

                      Expected behavior

                      When the user has not issued a turn, T3 Code should not initiate Codex API calls — especially anything that hits the responses_websocket / sse::responses streaming endpoints, which incur per-token plan consumption.

                      Actual behavior

                      While idle, T3 Code keeps a Codex app-server process alive identifying itself as clientInfo.name = "t3code_desktop" (set in apps/server/src/provider/Layers/CodexProvider.ts lines 241 and 280), and periodically refreshes its provider snapshot via makeManagedServerProvider.ts lines 141–146 — an unconditional Effect.forever(Effect.sleep(refreshInterval)) loop. CodexDriver.ts sets SNAPSHOT_REFRESH_INTERVAL = Duration.minutes(5).

                      The refresh itself calls probeCodexAppServerProvider which sends initialize, account/read, model/list, and skills/list. Those are not the costly ones on their own — but they keep the websocket warm and, in my observed window, also produced bursts of codex_api::endpoint::responses_websocket activity (e.g. 279 events in a single minute at 08:28 BR) while I was asleep. I don't have a smoking-gun trace of which T3 code path triggers those — that's where I need the maintainers to look. The strongest candidates:

                      • A stuck activeTurnId keeping ProviderSessionReaper from reaping the orphan session (ProviderSessionReaper.ts:64-71 short-circuits when activeTurnId != null). This is what PR reconcile provider session state and settle stuck turns #2666 already addresses.
                      • The Codex websocket warmup running on every refresh / reconnect.

                      Why the Claude fix doesn't cover this

                      Issue #2191 was closed because probeClaudeCapabilities() was changed to stop hitting the Anthropic API. The Codex equivalent (probeCodexAppServerProvider in apps/server/src/provider/Layers/CodexProvider.ts) was not given the same treatment — it still spawns a Codex app-server every refresh, which in turn handshakes against chatgpt.com. And the broader gating PR #2679 ("Add background activity policy and host power monitoring") is still OPEN, CONFLICTING, not merged — so even released builds remain affected.

                      Evidence

                      1. Burn timeline (Brazil time, UTC-3) — from ~/.codex/logs_2.sqlite

                      The last user-initiated Codex rollout file in ~/.codex/sessions/ is 2026-05-14T22:32 BR (<turn_aborted> as final event). Everything after this is non-user-initiated.

                      Hora BREventsNotes
                      14/05 22:5012,087tail of user session
                      15/05 01:4192first t3code_desktop calls to chatgpt.com (model/list)
                      15/05 03:32t3code_desktop calls account/rateLimits/read
                      15/05 03:48699t3code_desktop heavy refresh
                      15/05 08:28279 events on codex_api::endpoint::responses_websocket + 19 on codex_api::sse::responses (asleep, no input)
                      15/05 10:571,301sustained

                      2. t3code_desktop is the only non-Codex-official client_name seen

                      Distinct client_name values observed making outbound calls during the burn window:

                      • t3code_desktop — the only third-party identifier
                      • codex_desktop / codex_cli — official OpenAI Codex clients (user-driven during earlier daytime hours)

                      3. Burn stops the moment T3 is killed

                      After pkill -f "T3 Code" and trashing the app:

                      • 0 t3code_desktop log entries in the next 5 minutes.
                      • Subsequent Codex API attempts (from official Codex clients reconnecting) immediately get error.message="You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at May 18th, 2026 7:50 PM." — confirming the plan quota was the constrained resource.

                      4. Process tree right before the kill

                      PID ELAPSED COMMAND
                      36707 05:34:40 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)
                      36717 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=gpu-process --user-data-dir=.../t3code
                      36718 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=utility --utility-sub-type=network.mojom.NetworkService
                      36783 05:34:37 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha) /Applications/T3 Code (Alpha).app/Contents/Resources/app.asar/apps/server/dist/bin.mjs --bootstrap-fd 3
                      37178 05:34:30 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper (Renderer)
                      

                      apps/server/dist/bin.mjs (pid 36783) is the in-app server holding the Codex app-server channels open.

                      Suggested places to look

                      1. apps/server/src/provider/Layers/CodexProvider.ts:251probeCodexAppServerProvider. Gate this behind foreground client demand the same way Reduce idle work and disk churn with native resource diagnostics #2679 plans to gate makeManagedServerProvider, or change it to read account/model info from local Codex state instead of spawning a fresh app-server every 5 minutes.
                      2. apps/server/src/provider/Layers/ProviderSessionReaper.ts:64-71 — the activeTurnId != null short-circuit. PR reconcile provider session state and settle stuck turns #2666 addresses this; a stuck activeTurnId after <turn_aborted> means the reaper never tears down orphan sessions, and any background reconnect/warmup on that session is on the user's dime.
                      3. Whatever path triggers codex_api::endpoint::responses_websocket traffic from a non-user-initiated context. Most likely candidates: websocket warmup running on every refresh tick, or a stuck turn being silently retried.

                      Impact

                      Blocks work completely — drained 1000 purchased credits in ~14 hours of idle time. The user is now blocked from Codex use until 2026-05-18 19:50 BR (next plan window reset).

                      Version or commit

                      T3 Code (Alpha) — latest installer as of 2026-05-15 (the package.json in main reads 0.0.24).
                      Codex CLI spawned by T3: 0.131.0-alpha.9.

                      Environment

                      macOS Darwin 25.3.0 (Apple Silicon), ~/.codex auth via OAuth (auth_mode = "Chatgpt", Codex Pro plan).

                      Related

                      Happy to share raw logs_2.sqlite excerpts or specific timestamps on request.

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        No labels
                        No labels

                        Type

                        No type

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          [Bug]: Codex provider drains plan credits while T3 Code is idle in background (Codex equivalent of #2191) #2720

                          Description

                          @guilhermelippert

                          Before submitting

                          Area

                          apps/server

                          Summary

                          T3 Code (Alpha) drains the user's Codex Pro plan credits while idle in the background, even with the UI closed. In my case it burned 1000 purchased credits between ~01:00 BR and ~12:00 BR on 2026-05-15 while the app was minimized and the laptop was unattended. Activity pauses when the macOS lid is closed (system sleep) and resumes on wake — consistent with an in-app loop, not a server-side schedule.

                          The pattern matches the now-closed Claude bug #2191 ("Nightly: Claude Code burns tokens every 5 minutes while t3code is running idle"), but for the Codex provider. The fix Julius shipped for Claude in probeClaudeCapabilities() does not appear to extend to probeCodexAppServerProvider() in CodexProvider.ts.

                          Steps to reproduce

                          1. Have Codex Pro authenticated locally (codex login, populates ~/.codex/auth.json).
                          2. Launch T3 Code (Alpha), use it briefly, then minimize / close the window but leave the app running in the dock.
                          3. Watch ~/.codex/logs_2.sqlite:
                            SELECT datetime(ts,'unixepoch','-3 hours') AS brt, COUNT(*) AS events
                            FROM logs
                            WHERE feedback_log_body LIKE'%t3code_desktop%'GROUP BY strftime('%Y-%m-%d %H', datetime(ts,'unixepoch','-3 hours'))
                            ORDER BY brt;
                          4. Observe periodic model/list, account/rateLimits/read, and (less frequently but most expensive) responses_websocket activity with no corresponding user input. Killing every T3 process makes the t3code_desktop client_name disappear from the logs entirely.

                          Expected behavior

                          When the user has not issued a turn, T3 Code should not initiate Codex API calls — especially anything that hits the responses_websocket / sse::responses streaming endpoints, which incur per-token plan consumption.

                          Actual behavior

                          While idle, T3 Code keeps a Codex app-server process alive identifying itself as clientInfo.name = "t3code_desktop" (set in apps/server/src/provider/Layers/CodexProvider.ts lines 241 and 280), and periodically refreshes its provider snapshot via makeManagedServerProvider.ts lines 141–146 — an unconditional Effect.forever(Effect.sleep(refreshInterval)) loop. CodexDriver.ts sets SNAPSHOT_REFRESH_INTERVAL = Duration.minutes(5).

                          The refresh itself calls probeCodexAppServerProvider which sends initialize, account/read, model/list, and skills/list. Those are not the costly ones on their own — but they keep the websocket warm and, in my observed window, also produced bursts of codex_api::endpoint::responses_websocket activity (e.g. 279 events in a single minute at 08:28 BR) while I was asleep. I don't have a smoking-gun trace of which T3 code path triggers those — that's where I need the maintainers to look. The strongest candidates:

                          • A stuck activeTurnId keeping ProviderSessionReaper from reaping the orphan session (ProviderSessionReaper.ts:64-71 short-circuits when activeTurnId != null). This is what PR reconcile provider session state and settle stuck turns #2666 already addresses.
                          • The Codex websocket warmup running on every refresh / reconnect.

                          Why the Claude fix doesn't cover this

                          Issue #2191 was closed because probeClaudeCapabilities() was changed to stop hitting the Anthropic API. The Codex equivalent (probeCodexAppServerProvider in apps/server/src/provider/Layers/CodexProvider.ts) was not given the same treatment — it still spawns a Codex app-server every refresh, which in turn handshakes against chatgpt.com. And the broader gating PR #2679 ("Add background activity policy and host power monitoring") is still OPEN, CONFLICTING, not merged — so even released builds remain affected.

                          Evidence

                          1. Burn timeline (Brazil time, UTC-3) — from ~/.codex/logs_2.sqlite

                          The last user-initiated Codex rollout file in ~/.codex/sessions/ is 2026-05-14T22:32 BR (<turn_aborted> as final event). Everything after this is non-user-initiated.

                          Hora BREventsNotes
                          14/05 22:5012,087tail of user session
                          15/05 01:4192first t3code_desktop calls to chatgpt.com (model/list)
                          15/05 03:32t3code_desktop calls account/rateLimits/read
                          15/05 03:48699t3code_desktop heavy refresh
                          15/05 08:28279 events on codex_api::endpoint::responses_websocket + 19 on codex_api::sse::responses (asleep, no input)
                          15/05 10:571,301sustained

                          2. t3code_desktop is the only non-Codex-official client_name seen

                          Distinct client_name values observed making outbound calls during the burn window:

                          • t3code_desktop — the only third-party identifier
                          • codex_desktop / codex_cli — official OpenAI Codex clients (user-driven during earlier daytime hours)

                          3. Burn stops the moment T3 is killed

                          After pkill -f "T3 Code" and trashing the app:

                          • 0 t3code_desktop log entries in the next 5 minutes.
                          • Subsequent Codex API attempts (from official Codex clients reconnecting) immediately get error.message="You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at May 18th, 2026 7:50 PM." — confirming the plan quota was the constrained resource.

                          4. Process tree right before the kill

                          PID ELAPSED COMMAND
                          36707 05:34:40 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)
                          36717 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=gpu-process --user-data-dir=.../t3code
                          36718 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=utility --utility-sub-type=network.mojom.NetworkService
                          36783 05:34:37 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha) /Applications/T3 Code (Alpha).app/Contents/Resources/app.asar/apps/server/dist/bin.mjs --bootstrap-fd 3
                          37178 05:34:30 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper (Renderer)
                          

                          apps/server/dist/bin.mjs (pid 36783) is the in-app server holding the Codex app-server channels open.

                          Suggested places to look

                          1. apps/server/src/provider/Layers/CodexProvider.ts:251probeCodexAppServerProvider. Gate this behind foreground client demand the same way Reduce idle work and disk churn with native resource diagnostics #2679 plans to gate makeManagedServerProvider, or change it to read account/model info from local Codex state instead of spawning a fresh app-server every 5 minutes.
                          2. apps/server/src/provider/Layers/ProviderSessionReaper.ts:64-71 — the activeTurnId != null short-circuit. PR reconcile provider session state and settle stuck turns #2666 addresses this; a stuck activeTurnId after <turn_aborted> means the reaper never tears down orphan sessions, and any background reconnect/warmup on that session is on the user's dime.
                          3. Whatever path triggers codex_api::endpoint::responses_websocket traffic from a non-user-initiated context. Most likely candidates: websocket warmup running on every refresh tick, or a stuck turn being silently retried.

                          Impact

                          Blocks work completely — drained 1000 purchased credits in ~14 hours of idle time. The user is now blocked from Codex use until 2026-05-18 19:50 BR (next plan window reset).

                          Version or commit

                          T3 Code (Alpha) — latest installer as of 2026-05-15 (the package.json in main reads 0.0.24).
                          Codex CLI spawned by T3: 0.131.0-alpha.9.

                          Environment

                          macOS Darwin 25.3.0 (Apple Silicon), ~/.codex auth via OAuth (auth_mode = "Chatgpt", Codex Pro plan).

                          Related

                          Happy to share raw logs_2.sqlite excerpts or specific timestamps on request.

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            No labels
                            No labels

                            Type

                            No type

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              [Bug]: Codex provider drains plan credits while T3 Code is idle in background (Codex equivalent of #2191) #2720

                              Description

                              @guilhermelippert

                              Before submitting

                              Area

                              apps/server

                              Summary

                              T3 Code (Alpha) drains the user's Codex Pro plan credits while idle in the background, even with the UI closed. In my case it burned 1000 purchased credits between ~01:00 BR and ~12:00 BR on 2026-05-15 while the app was minimized and the laptop was unattended. Activity pauses when the macOS lid is closed (system sleep) and resumes on wake — consistent with an in-app loop, not a server-side schedule.

                              The pattern matches the now-closed Claude bug #2191 ("Nightly: Claude Code burns tokens every 5 minutes while t3code is running idle"), but for the Codex provider. The fix Julius shipped for Claude in probeClaudeCapabilities() does not appear to extend to probeCodexAppServerProvider() in CodexProvider.ts.

                              Steps to reproduce

                              1. Have Codex Pro authenticated locally (codex login, populates ~/.codex/auth.json).
                              2. Launch T3 Code (Alpha), use it briefly, then minimize / close the window but leave the app running in the dock.
                              3. Watch ~/.codex/logs_2.sqlite:
                                SELECT datetime(ts,'unixepoch','-3 hours') AS brt, COUNT(*) AS events
                                FROM logs
                                WHERE feedback_log_body LIKE'%t3code_desktop%'GROUP BY strftime('%Y-%m-%d %H', datetime(ts,'unixepoch','-3 hours'))
                                ORDER BY brt;
                              4. Observe periodic model/list, account/rateLimits/read, and (less frequently but most expensive) responses_websocket activity with no corresponding user input. Killing every T3 process makes the t3code_desktop client_name disappear from the logs entirely.

                              Expected behavior

                              When the user has not issued a turn, T3 Code should not initiate Codex API calls — especially anything that hits the responses_websocket / sse::responses streaming endpoints, which incur per-token plan consumption.

                              Actual behavior

                              While idle, T3 Code keeps a Codex app-server process alive identifying itself as clientInfo.name = "t3code_desktop" (set in apps/server/src/provider/Layers/CodexProvider.ts lines 241 and 280), and periodically refreshes its provider snapshot via makeManagedServerProvider.ts lines 141–146 — an unconditional Effect.forever(Effect.sleep(refreshInterval)) loop. CodexDriver.ts sets SNAPSHOT_REFRESH_INTERVAL = Duration.minutes(5).

                              The refresh itself calls probeCodexAppServerProvider which sends initialize, account/read, model/list, and skills/list. Those are not the costly ones on their own — but they keep the websocket warm and, in my observed window, also produced bursts of codex_api::endpoint::responses_websocket activity (e.g. 279 events in a single minute at 08:28 BR) while I was asleep. I don't have a smoking-gun trace of which T3 code path triggers those — that's where I need the maintainers to look. The strongest candidates:

                              • A stuck activeTurnId keeping ProviderSessionReaper from reaping the orphan session (ProviderSessionReaper.ts:64-71 short-circuits when activeTurnId != null). This is what PR reconcile provider session state and settle stuck turns #2666 already addresses.
                              • The Codex websocket warmup running on every refresh / reconnect.

                              Why the Claude fix doesn't cover this

                              Issue #2191 was closed because probeClaudeCapabilities() was changed to stop hitting the Anthropic API. The Codex equivalent (probeCodexAppServerProvider in apps/server/src/provider/Layers/CodexProvider.ts) was not given the same treatment — it still spawns a Codex app-server every refresh, which in turn handshakes against chatgpt.com. And the broader gating PR #2679 ("Add background activity policy and host power monitoring") is still OPEN, CONFLICTING, not merged — so even released builds remain affected.

                              Evidence

                              1. Burn timeline (Brazil time, UTC-3) — from ~/.codex/logs_2.sqlite

                              The last user-initiated Codex rollout file in ~/.codex/sessions/ is 2026-05-14T22:32 BR (<turn_aborted> as final event). Everything after this is non-user-initiated.

                              Hora BREventsNotes
                              14/05 22:5012,087tail of user session
                              15/05 01:4192first t3code_desktop calls to chatgpt.com (model/list)
                              15/05 03:32t3code_desktop calls account/rateLimits/read
                              15/05 03:48699t3code_desktop heavy refresh
                              15/05 08:28279 events on codex_api::endpoint::responses_websocket + 19 on codex_api::sse::responses (asleep, no input)
                              15/05 10:571,301sustained

                              2. t3code_desktop is the only non-Codex-official client_name seen

                              Distinct client_name values observed making outbound calls during the burn window:

                              • t3code_desktop — the only third-party identifier
                              • codex_desktop / codex_cli — official OpenAI Codex clients (user-driven during earlier daytime hours)

                              3. Burn stops the moment T3 is killed

                              After pkill -f "T3 Code" and trashing the app:

                              • 0 t3code_desktop log entries in the next 5 minutes.
                              • Subsequent Codex API attempts (from official Codex clients reconnecting) immediately get error.message="You've hit your usage limit. Visit https://chatgpt.com/codex/settings/usage to purchase more credits or try again at May 18th, 2026 7:50 PM." — confirming the plan quota was the constrained resource.

                              4. Process tree right before the kill

                              PID ELAPSED COMMAND
                              36707 05:34:40 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha)
                              36717 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=gpu-process --user-data-dir=.../t3code
                              36718 05:34:38 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper --type=utility --utility-sub-type=network.mojom.NetworkService
                              36783 05:34:37 /Applications/T3 Code (Alpha).app/Contents/MacOS/T3 Code (Alpha) /Applications/T3 Code (Alpha).app/Contents/Resources/app.asar/apps/server/dist/bin.mjs --bootstrap-fd 3
                              37178 05:34:30 /Applications/T3 Code (Alpha).app/Contents/Frameworks/.../T3 Code (Alpha) Helper (Renderer)
                              

                              apps/server/dist/bin.mjs (pid 36783) is the in-app server holding the Codex app-server channels open.

                              Suggested places to look

                              1. apps/server/src/provider/Layers/CodexProvider.ts:251probeCodexAppServerProvider. Gate this behind foreground client demand the same way Reduce idle work and disk churn with native resource diagnostics #2679 plans to gate makeManagedServerProvider, or change it to read account/model info from local Codex state instead of spawning a fresh app-server every 5 minutes.
                              2. apps/server/src/provider/Layers/ProviderSessionReaper.ts:64-71 — the activeTurnId != null short-circuit. PR reconcile provider session state and settle stuck turns #2666 addresses this; a stuck activeTurnId after <turn_aborted> means the reaper never tears down orphan sessions, and any background reconnect/warmup on that session is on the user's dime.
                              3. Whatever path triggers codex_api::endpoint::responses_websocket traffic from a non-user-initiated context. Most likely candidates: websocket warmup running on every refresh tick, or a stuck turn being silently retried.

                              Impact

                              Blocks work completely — drained 1000 purchased credits in ~14 hours of idle time. The user is now blocked from Codex use until 2026-05-18 19:50 BR (next plan window reset).

                              Version or commit

                              T3 Code (Alpha) — latest installer as of 2026-05-15 (the package.json in main reads 0.0.24).
                              Codex CLI spawned by T3: 0.131.0-alpha.9.

                              Environment

                              macOS Darwin 25.3.0 (Apple Silicon), ~/.codex auth via OAuth (auth_mode = "Chatgpt", Codex Pro plan).

                              Related

                              Happy to share raw logs_2.sqlite excerpts or specific timestamps on request.

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                No labels
                                No labels

                                Type

                                No type

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions