Why Codex Can Open 100 Browser Tabs — and What Is Really Happening
Persistent sessions, parallel QA, retries and Chromium process lifetime
A local evidence-based analysis of memory pressure in the in-app browser
Practical safeguards for browser-heavy Codex workflows
Codex browser tabs, resource management, renderer processes, memory pressure and cleanup

$ inspect codex_browser --resource-lifecycle
> tabs: routes × viewports × iterations × agents × retries
> pressure: renderers / memory / swap / timeouts
> controls: reuse / ownership / cleanup / hard-limitAfter ChatGPT Work, Codex, Computer Use and the in-app browser were brought together in one desktop application, Codex gained the ability to edit code, open pages, inspect interfaces, take screenshots, navigate between routes and run several tasks in parallel.
That is a major expansion of what an AI assistant can do. It also introduces a resource-management problem: during long-running work, the built-in browser can accumulate dozens—or even more than a hundred—open tabs. On a MacBook with 16 GB of unified memory, this can eventually cause high memory pressure, heavy swap usage, slower tool calls and degraded system performance.
> Key finding: local inspection suggests that this is not merely a visual UI issue. It is connected to how Codex creates, stores, reuses and disposes browser sessions.
The observations below describe one inspected installation and a plausible mechanism, not a universal benchmark for every Codex version or Mac.
The in-app browser is a full Chromium environment
The built-in browser is not a lightweight preview panel. The inspected installation was running ChatGPT/Codex version 26.707.61608, build 5200, Chromium Framework 150.0.7871.115, separate renderer/GPU/network/storage processes and a dedicated browser profile under ~/Library/Application Support/Codex.
In practical terms, ChatGPT contains a real Chromium runtime. It maintains pages, navigation state, site data, downloads, authentication state and rendering processes. OpenAI documents that the built-in browser uses its own browser state rather than the user’s normal Chrome profile and is intended for public sites, local development, visual review and frontend testing.
How a normal QA loop becomes 100 tabs
A routine website loop can be enough:
- Codex changes a page.
- It opens the local URL and takes a screenshot.
- It inspects the result and changes the code again.
- It opens the page again for desktop and mobile.
- It moves to another route and repeats the cycle.
If every check creates a new tab instead of navigating an existing one, one iteration produces several tabs:
5 routes × 2 viewport sizes × 3 iterations = 30 tabs
30 tabs × 3 parallel agents = 90 tabsA few retries after timeouts can push the total beyond 100. The multiplication model is simple:
routes × viewports × iterations × agents × retriesTabs are attached to conversations and persisted
Local application state showed browser tabs stored across actions and conversations. The inspected state contained 23 unique browserTabId values, tabs associated with 16 conversations, up to four saved tabs inside one conversation, plus stored URLs, titles, navigation state, runtime state and conversation identifiers.
This indicates that the browser is not organized around one reusable global tab. A task can retain its own pages. Persistence is useful because a user can return to an old task and continue from the same state. Without an effective cleanup policy, however, retained tabs grow with the number of tasks and verification cycles.
Some browser sessions are intentionally kept alive
One important log entry was:
IAB_LIFECYCLE captured session route ... disposeAfterSessionActivity=falseThe value disposeAfterSessionActivity=false indicates that the corresponding browser session should not be destroyed automatically as soon as activity ends. Logs also included lifecycle events for registering, rebinding and removing browser sidebar windows.
A removal mechanism therefore exists, but cleanup does not happen after every verification step and does not necessarily dispose of every tab.
Browser processes accumulate too
At the time of inspection, the application had 11 active Codex (Renderer) processes, roughly 33 ChatGPT/Codex-related processes in total, multiple SkyComputerUseClient processes and processes with different start times. Combined resident memory was approximately 3.6 GiB RSS.
This does not mean every tab always creates exactly one renderer. Chromium can group pages or isolate them by site and origin. The broader relationship remains:
> More tabs → more active page state → more renderer and helper processes → greater memory usage.
Local React or Next.js applications, WebGL scenes, maps, video, complex animation and persistent WebSocket connections can make each retained page significantly more expensive.
Parallel agents multiply the problem
Codex is designed to run multiple tasks in parallel. Several agents may open pages, inspect different routes, capture screenshots and repeat checks at the same time.
Parallelism itself is not the problem. The problem appears when every agent creates its own tabs and those tabs remain alive after the work is complete.
Retries can create a feedback loop
Opening a page and receiving confirmation are separate events. A failure can unfold like this:
- The open command succeeds and the tab appears.
- The tool does not receive confirmation in time.
- The operation times out and is retried.
- A duplicate tab appears.
The inspected logs contained many timeout and retry references. Not all were browser-related, so it would be wrong to claim that every timeout created a tab. Still, the mechanism is a strong explanation for batches of identical pages appearing within seconds.
It can become self-reinforcing:
more tabs → memory and CPU pressure → slower operations → timeouts → retries → more tabsThis is not an M4 performance problem
Apple’s M4 is capable of running Codex, local development tools and several browser pages. The limiting factor in this scenario is unified memory, not raw CPU performance.
A 16 GB machine may simultaneously run macOS, ChatGPT/Codex, Chromium renderers, an IDE, development servers, terminals, Docker, a normal browser and other applications. Once memory pressure rises, macOS relies on swap. A fast processor cannot remove the latency caused by moving memory pages between RAM and storage.
The same architecture can affect a more powerful Mac. Additional memory delays the point at which degradation becomes visible; it does not fix lifecycle management.
Is there a configurable tab limit?
No user-facing hard tab-limit setting was found in the inspected Codex configuration. There were no controls equivalent to:
browser.max_tabs = 10
browser.reuse_tabs = true
browser.close_tabs_on_task_end = trueThe reviewed public documentation did not specify a maximum in-app tab count, automatic least-recently-used eviction, mandatory tab reuse, task-completion cleanup, a renderer-process limit or a memory-pressure threshold that stops new tabs.
Agent thread limits are not browser-tab limits. One agent can still generate many tabs during a long verification loop.
Is the model responsible, or the application?
Responsibility is split across two layers.
The model and task instructions choose a strategy
They decide whether to open a page, reuse an existing one, inspect routes in parallel or repeat an operation.
The application and browser tooling manage physical resources
They create and store the tab, assign its browserTabId, attach it to a conversation, close the page, terminate renderers, dispose inactive sessions and enforce resource limits.
The model may choose an inefficient “new tab for every check” strategy. Allowing a very large number of active tabs to accumulate without reliable cleanup is nevertheless an application-level resource-management concern.
What a safer architecture should provide
Tab reuse
If the required origin is already open, navigate the existing tab rather than creating another one.
Clear ownership and a hard limit
The application should know which task created each tab and whether it can be closed. After a defined threshold, it should reuse, suspend or request confirmation before creating more.
Cleanup after task completion
Temporary pages should close when verification finishes, a task is cancelled or archived, or a session remains idle beyond a safe threshold.
Idempotent retries
If navigation succeeded but acknowledgement was lost, a retry should reconnect to the existing tab rather than create a duplicate.
Memory-pressure protection
Under pressure, the application should suspend background pages, unload renderers, warn the user and stop uncontrolled tab creation.
A temporary instruction-level safeguard
Until the application enforces a system-level limit, add explicit browser rules to project instructions:
- Reuse the existing browser binding and current tab.
- Navigate the current tab instead of opening a new one.
- Keep no more than three browser tabs per task.
- Close temporary tabs immediately after verification.
- Do not run parallel browser QA unless explicitly requested.
- Before opening a tab, check whether the target URL is already open.
- When a task ends or is cancelled, close all tabs created by that task.
This reduces unnecessary pages but is not a guarantee. Tool retries, restoration and lifecycle failures can still create duplicates.
Conclusion
The appearance of 50–100 tabs is best explained by several factors working together: new tabs instead of navigation, per-conversation persistence, sessions with disposeAfterSessionActivity=false, retained renderer and Computer Use processes, parallel checks, timeout retries and no user-configurable hard limit found in the inspected setup.
The central issue is not simply MacBook performance or model behavior. It is the combination of persistent browser sessions with insufficient control over tab count and resource lifetime. A full working browser needs tab reuse, background suspension, idempotent navigation and predictable cleanup.