[Desktop/macOS] Search synchronously Markdown-parses all local thread titles, blocking renderer for 82s

Open 💬 0 comments Opened Jul 27, 2026 by Slava-AV

Summary

Opening global search in Codex Desktop can synchronously normalize every local thread title as Markdown/GFM on the renderer main thread. With a large local history containing oversized titles, this blocks the UI for more than a minute and can make search unusable for over five minutes.

This is related to #24510, but adds a narrower, trace-confirmed renderer root cause and a controlled A/B test.

Environment

  • Codex Desktop: 26.721.41059 (build 5848)
  • Chromium: 150.0.7871.128
  • macOS: 26.5.2
  • Hardware: Apple Silicon, 24 GiB RAM
  • Onset: sudden, shortly after the current app update (correlation only; a previous-build A/B was not performed)

Symptoms

  • App launch became multi-minute.
  • Opening global search took about 50 seconds to show the search screen.
  • The search UI then remained fully unresponsive for more than 5 minutes, so text could not be entered.

Reproduction

  1. Use a local Codex profile with approximately 4,000 threads and very large values stored as thread titles.
  2. Launch Codex Desktop.
  3. Open global search.
  4. Observe that the renderer stops responding before the search field becomes usable.

Expected behavior

Search should open promptly and remain interactive regardless of total history size or malformed/oversized title metadata.

Actual behavior

The renderer main thread is blocked by synchronous title processing. Disk, SQLite, network, and garbage collection are not the dominant cost in the captured trace.

Performance trace

A trace was recorded with Codex's built-in Performance Trace Recording and uploaded to OpenAI.

  • Feedback ID: 019fa293-79c9-7b42-a513-786dada9af94
  • Preserved trace size: 260,447,716 bytes
  • SHA-256: 10b4b67d0c94f03661659274cab5ed3f21b4c7ac635ed38ac5a6119e5d31e071
  • Trace events: 1,171,040
  • Active span: 87.129 s

Renderer main-thread blocking spans:

  1. 54.876 s under click dispatch → microtasks → function call
  2. Immediately followed by 27.635 s under MessagePort::Accept → React scheduler function call

The hot packaged-code path was:

Rru (command menu/search)
  -> gKa (maps all conversations)
  -> O_ / vSt / bSt
  -> D_
  -> Qg
  -> micromark/mdast + GFM normalization

Read-only inspection of the signed packaged JavaScript showed:

  • gKa iterates all conversationsMeta entries.
  • Each local thread is passed through O_(e).
  • bSt passes the full e.title.
  • D_(n) calls Qg(n).
  • Qg parses Markdown/GFM and strips it to structured/plain display text.

The CPU profile attributed approximately 98.6% of sampled CPU to this packaged application stack. The dominant exclusive work was micromark tokenization (about 43%) and the GFM autolink helper (about 38%). GC was negligible (about 0.16 s).

Local state measurements before isolation

The database passed PRAGMA integrity_check.

  • Threads: 3,993
  • Total title characters: 48,711,500
  • Maximum title length: 722,414
  • Titles >= 10,000 characters: 593
  • Titles >= 100,000 characters: 120
  • Rows where title = first_user_message = preview: 3,473
  • The 593 oversized titles contained 47,248,933 characters, about 96.4% of all title text.

A direct SQLite LIKE query over the same state completed in about 0.17 s, so the multi-minute UI stall is not explained by the database query itself.

Controlled, reversible A/B tests

No data was deleted.

  • Isolating Electron caches: no change
  • Isolating only state_5.sqlite: no change; it rebuilt from session history
  • Isolating session history + session index + state database: Codex became instant
  • Restoring the history: the lag returned
  • Isolating only the 593 session files whose stored titles were >= 10,000 characters:
  • Live thread count: 3,400
  • Total title characters: 1,773,155
  • Maximum title length: 9,633
  • Titles >= 10,000 characters: 0
  • Launch and search became instant with no observable lag

This establishes oversized title metadata as necessary for this reproduction and the synchronous Markdown/GFM normalization path as the renderer bottleneck.

Suggested fixes

  1. Enforce a hard, small title-length limit when titles enter local state.
  2. Do not copy complete prompts into title, and do not duplicate unbounded content across title, first_user_message, and preview.
  3. Do not Markdown-parse every conversation title when search opens; use a bounded plain-text title projection.
  4. Cache normalized display titles and update them lazily or off the renderer main thread.
  5. Virtualize/paginate search and normalize only visible results.
  6. Add a regression test with roughly 4,000 threads, 50 million title characters, and at least one 700,000-character title.
  7. Add telemetry for renderer long tasks and aggregate title bytes processed when opening search.

Workaround

Reversibly quarantining only session files with titles >= 10,000 characters removes the lag, but temporarily hides those tasks. This is diagnostic evidence, not a safe general-purpose fix.

Related issues

  • #24510 — unbounded active thread metadata and local history/list processing
  • #34555 — renderer freezes after referencing large local chats
  • #35236 — renderer hangs after adding a long cloud chat
  • #18693 — performance collapse with large histories

Raw conversation contents, titles, usernames, and local filesystem paths are intentionally omitted. The uploaded trace should be available internally using the Feedback ID above; the preserved local trace can be shared privately if needed.

View original on GitHub ↗