Opening a thread with large reasoning blocks freezes the desktop window#22988

opened12d agobyFfredlarochelle

What happened

Opening an existing OpenCode thread in the Linux desktop client freezes the T3 Code window for several seconds. The desktop client connects to a remote Linux server. Other desktop windows remain responsive

Diagnosis

Collapsed reasoning previews process the entire reasoning block:

The earlier isolated Chromium benchmark reproduced multi-second preview transformations on the affected reasoning blocks. This strongly implicates the preview transformer, but is not a profile of the laptop window.

The same transformer remains in main at 680152e27b2a (https://github.com/pingdotgg/t3code/blob/680152e27b2a961686389ca69a1557cbb2c55f08/apps/web/src/components/chat/TimelineWorkEntryRow.logic.ts#L192) and nightly 0.1.2-nightly.20261021.3473. Version 0.1.1 remains the latest stable release (https://github.com/pingdotgg/t3code/releases/latest).

Steps to reproduce

Steps to reproduce

  1. Open the Linux desktop client connected to the remote server.
  2. Open the affected OpenCode thread, which contains reasoning blocks of 190,547 and 135,976 characters.
  3. Observe several seconds of window unresponsiveness while opening the thread.

For an isolated reproduction, parse those reasoning blocks with the installed 0.1.1 Markdown parser, then time remarkThoughtPreview("Thought") separately on a freshly parsed tree for each sample.

A portable, sanitized input fixture and laptop renderer profile are not yet available.

Version

Desktop: 0.1.1, confirmed by the user from the UI, which reports it is up to date. The desktop binary/build hash was not independently inspected. Remote server: 0.1.1, verified through its live environment endpoint. Triage CLI: 0.1.1

Environment

Linux laptop desktop client → remote Linux x64 server; OpenCode provider. Server: Fedora 43, kernel 7.2.11-100.fc43.x86_64, Node v26.8.2. These are server facts; the laptop distribution and hardware were not collected.

Evidence

shell
1Earlier investigation by the user on the remote server found 117 timeline items and the following isolated Chromium results, with three measurements per input:
2
3| Reasoning characters | Markdown parsing | Preview transformation |
4|---:|---:|---:|
5| 190,547 | approximately 100 ms | 5.35–5.39 seconds |
6| 135,976 | not supplied | 2.84–2.86 seconds |
7
8These measurements were supplied from the earlier investigation and were not rerun during this triage. They are isolated server-side Chromium measurements, not laptop window timings.
9
10This triage independently confirmed both reasoning lengths. Read-only database queries currently return 125 persisted turn items in 0.72–1.98 ms across three reads. Persisted turn items and displayed timeline items are different counts.

Related issues

No confirmed duplicate found. - #4596 (https://github.com/pingdotgg/t3code/issues/4596): similar thread-opening freeze, attributed to event replay and repeated state publication. - #19992 (https://github.com/pingdotgg/t3code/issues/19992): quadratic rescanning during server-side assistant streaming; a different component and trigger.

Fix applied or workaround

None. Investigation was read-only. Collapsing reasoning still invokes the expensive preview path

Filed by

Prepared by Codex (GPT-6.1-Sol, high reasoning) via t3 triage

Synced from GitHub

Same freeze here on the Linux desktop app. Numbers from a real thread:

  • The thread has one reasoning item of 299,448 chars, from a provider loop (~38k lines, mostly Hmm.).
  • Headless replay of the collapsed-preview path on main (5b35402623) with that block: 36.9s, almost all of it remarkThoughtPreview rescanning the growing string. Capping the preview input at 2k chars and normalizing whitespace once: 7ms. #23001 takes the same approach, so it should cover this.

One gap #23001 leaves open: projectTurnItemForWire (apps/server/src/orchestration-v2/WireProjection.ts) caps command output, diffs and subagent text, but reasoning goes through unchanged. Clients get the whole trace in the snapshot and in every turn-item.updated. I prototyped capping it at 32KB with a textTruncated flag and fetching the rest through getTurnItem on expand (256KB, like command output). On this thread, that cut the big item from 299,448 to 32,797 chars on the wire. Only 47 of 43,619 reasoning items in my local data are over 32KB, so it's a small follow-up. Happy to open a PR if maintainers want it.