Repository navigation
[Bug]: React #185 loop in the composer context strip label compaction (BranchToolbar measure -> setOverflows), 0.0.43-nightly.20260921.2044 #12891
Description
Activity
Triage: accepted as a real composer-strip measurement loop. Not a duplicate of #11308.
The stack decode is correct against current
main(unchanged since0.0.43-nightly.20260921.2044/5781b5240):_chat-…: setOverflows(nextOverflows)is the last statement ofmeasure()inuseLabelsOverflow(apps/web/src/components/BranchToolbar.tsx)- the caller is the dependency-free
useLayoutEffect(() => { measure(); }) - React Add drag-and-drop project reordering to the sidebar #185 is
Maximum update depth exceeded
setOverflowsis a boolean, so React bails out when the decision is unchanged. The crash only happens when the compact pass and the expanded pass disagree aboutneededby more thanCONTEXT_STRIP_COMPACT_EXPAND_HYSTERESIS_PX(16) inresolveContextStripLabelsCompact(BranchToolbar.logic.ts). Each layout effect then flips the other until React stops at 50 nested updates. Reduce Motion skipping the[overflows]width animation matches the code; this is measurement-only.This is the same family as #9390 / #9393 / #9481 (strip layout effects writing state that changes the next measure), but not the same loop. Those were strip labels vs resting composer controls. #11308 is
@legendapp/listin theProviderModelPickerchunk.Leading reconstruction (same caution as the report: mechanism from source, not a captured width trace):
#12805 is in this nightly and changed the branch trigger that
measure()reads from onetruncatespan toMiddleTruncate(headmin-w-0 truncate+shrink-0tail) inBranchToolbarBranchSelector.tsx. Env / workspace labels are still the old single span.measure()still recovers hidden text asmax(subtree scrollWidth) - visible width. That describes one clipped span. For a split label that still fits undermax-w-[240px]:- expanded:
offsetWidthis head+tail, reservation is 0 → contributes the full string - compact (
group-data-[compact]/composer-context:max-w-0): reservation ismax(head, tail, wrapper)→ typically head only
The gap is about one tail (default 10 chars, last path segment up to 16). At
text-xsthat is larger than 16px, so there is a real flapping band. Short names that never split, and names already hard-capped at 240px, should agree and not loop. That also matches a one-shot crash that retry / a slightly different width escaped.A leftover #9393 fight with the resting-controls host would also exit through this
setOverflows. Less likely as the new trigger; that path already has natural-width reservation. #12841 would remove that host and does not fix compact-vs-expanded label math.Fix: make
neededindependent of the currentdata-compactDOM (sum of the MiddleTruncate parts, or an unclipped probe — notmaxover subtreescrollWidth). Do not just raise the 16px hysteresis. Add a regression that compact vs expanded needed widths for a split-but-under-240px branch label agree within hysteresis, then park the strip in that band with Reduce Motion on.Window size and the branch / env / workspace strings from the crashed thread would confirm the band; not required to start.
- addedacceptedfeature request acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Sep 21, 2026 Follow-up with facts I did not have when I filed this, after the crash recurred:
Frequency. It is not a one-off. It now reproduces on ordinary timeline scrolling in many threads, and resizing the window does not move it out of the band. Raising Impact to "Major degradation or frequent failure" accordingly. Retry and "Reload app" both recover, and a reloaded thread crashes again on the next scroll.
Build contents.
0.0.43-nightly.20260921.2044is5781b5240bd5. Two commits from the same night both touch this strip and are both ancestors of the build (confirmed withgit merge-base --is-ancestor, and by finding their minified fingerprints in the shipped chunks):- feat(web): truncate branch names and paths in the middle #12805 (
f7efb5354a) - branch label becomesMiddleTruncate, discussed above. - fix(web): keep composer controls visible while they fit #12837 (
6cc7f7006e, "keep composer controls visible while they fit") - rewritesresolveRestingComposerControlsLayoutinto a stepped fit (icon-only labels first, then overflow) and changesmeasureRestingComposerControlsto derivenatural/iconOnlyblock widths from one tree. The comment that PR removed is a description of this exact failure: "The composer re-measures on every render, so without that margin a host sitting exactly on a threshold flips a block in and out until React gives up with 'Maximum update depth exceeded'." The new code keepsRESTING_CONTROLS_SLACK_PX = 1only on promotions (step < previousStep).
Why scroll.
shouldUseRestingComposerLayoutrests the composer only on a timeline scroll gesture (isScrollCollapsed). Resting relocates the controls into[data-chat-resting-composer-controls-host]inside the context strip, and from that moment two dependency-free layout effects read the same DOM and write state that changes each other's inputs:BranchToolbarmeasure()reservesresolveRestingComposerControlsNaturalWidth(hostedMeasurement)and togglesoverflows, which changes the labels'max-wand therefore the host'sflex-1width;ChatComposerthen re-fits its controls to the newhostWidth, which changes the DOMmeasure()reads next. The thrower in the stack is whicheversetStatelands on the 50th nested update, sosetOverflowsbeing on top does not by itself say which side is inconsistent.The thread that crashed was on branch
feat/quiz-images-migrate(24 chars, sosplitForMiddleTruncatedoes split it: headfeat/quiz-imag, tailes-migrate), in a git project with the environment indicator shown. Nothing was streaming in that thread at the time; other threads on the same server were running turns.No commit on
mainafter5781b5240bd5touchesBranchToolbar*,ChatComposer.tsx,composerFooterLayout.tsorrestingComposerControlsMeasurement.tsas of this comment, so the next nightly will carry the same code.- feat(web): truncate branch names and paths in the middle #12805 (
Same build and identical stack offsets on Fedora 44/KDE Wayland. In my case, selecting one persisted thread reproduces the crash every time, no scrolling or streaming required, and retry/reload does not recover. The provider turn completed normally and the session remains
readywith no error.
T3 Code (Nightly) 0.0.43-nightly.20260921.2044 Path: /e4f522e2-b083-4e05-a6fe-bc1ccff59006/95bbe62a-d389-4caf-8167-587804a234ed Time: 2026-09-21T10:44:21.985Z Error: Minified React error #185; visit https://react.dev/errors/185 for the full message or use the non-minified dev environment for full errors and additional helpful warnings. at fi (t3code://app/assets/dist-BVnGsFIt.js:26:27485) at li (t3code://app/assets/dist-BVnGsFIt.js:26:27010) at Is (t3code://app/assets/dist-BVnGsFIt.js:26:58533) at Fs (t3code://app/assets/dist-BVnGsFIt.js:26:58155) at t3code://app/assets/_chat-DOCEAHoD.js:8:59203 at t3code://app/assets/_chat-DOCEAHoD.js:8:59884 at Uc (t3code://app/assets/dist-BVnGsFIt.js:26:91668) at sl (t3code://app/assets/dist-BVnGsFIt.js:26:96139) at xl (t3code://app/assets/dist-BVnGsFIt.js:26:104933) at sl (t3code://app/assets/dist-BVnGsFIt.js:26:96697)I hit the same issue on T3 Code (Nightly)
0.0.43-nightly.20260921.2044with the identical React #185 stack.Time:
2026-09-21T18:29:35.129Z
Path:/50a3196d-6590-4f7c-a1b6-4b089c3cd081/a7ac8843-5d15-4c14-b4c1-b6d567c9b756Hit this twice today on MacOS desktop nightly, on two different threads in the same environment. First on
0.0.43-nightly.20260921.2058at 2026-09-21T18:27:09.545Z, while scrolling a thread. The renderer died once a certain point in the timeline was on screen. Updated, then hit it again on0.0.43-nightly.20260921.2071at 2026-09-21T21:04:13.182Z.Short 15s video of the scroll that reproduces it: https://www.loom.com/share/b9e69a4b8c324fc9a97bac7b5e27668e
Sorry about the music. I did not realize the recording was picking up audio.
The asset hashes changed on each nightly (
2044wasdist-BVnGsFIt.js/_chat-DOCEAHoD.js,2058wasdist-Dd0QIH1a.js/_chat-DA5ugVFK.js,2071isdist-7UkdRZDe.js/_chat-f-Exmpka.js), but the offsets did not. Same React #185 frames, and the same two chat frames:_chat-….js:8:59203and:8:59884. Both builds still die insetOverflowsinside the dependency-freemeasure()layout effect. No pull request is linked on this issue.2058:Error: Minified React error #185; visit https://react.dev/errors/185 for the full message or use the non-minified dev environment for full errors and additional helpful warnings. at fi (t3code://app/assets/dist-Dd0QIH1a.js:26:27485) at li (t3code://app/assets/dist-Dd0QIH1a.js:26:27010) at Is (t3code://app/assets/dist-Dd0QIH1a.js:26:58533) at Fs (t3code://app/assets/dist-Dd0QIH1a.js:26:58155) at t3code://app/assets/_chat-DA5ugVFK.js:8:59203 at t3code://app/assets/_chat-DA5ugVFK.js:8:59884 at Uc (t3code://app/assets/dist-Dd0QIH1a.js:26:91668) at sl (t3code://app/assets/dist-Dd0QIH1a.js:26:96139) at xl (t3code://app/assets/dist-Dd0QIH1a.js:26:104933) at sl (t3code://app/assets/dist-Dd0QIH1a.js:26:96697)2071:Error: Minified React error #185; visit https://react.dev/errors/185 for the full message or use the non-minified dev environment for full errors and additional helpful warnings. at fi (t3code://app/assets/dist-7UkdRZDe.js:26:27485) at li (t3code://app/assets/dist-7UkdRZDe.js:26:27010) at Is (t3code://app/assets/dist-7UkdRZDe.js:26:58533) at Fs (t3code://app/assets/dist-7UkdRZDe.js:26:58155) at t3code://app/assets/_chat-f-Exmpka.js:8:59203 at t3code://app/assets/_chat-f-Exmpka.js:8:59884 at Uc (t3code://app/assets/dist-7UkdRZDe.js:26:91668) at sl (t3code://app/assets/dist-7UkdRZDe.js:26:96139) at xl (t3code://app/assets/dist-7UkdRZDe.js:26:104933) at sl (t3code://app/assets/dist-7UkdRZDe.js:26:96697)PS. Fix tried locally on current main (
371b52d9d), as an uncommitted diff: +71 −8 acrossBranchToolbar.tsx,BranchToolbar.logic.ts, andBranchToolbar.logic.test.ts. It is not in the2058or2071nightlies, and it is not opened as a PR. It follows the note above: do not raise the 16px hysteresis.measure()was reserving hidden label text withmax(subtree scrollWidth). AMiddleTruncateis two text leaves. Oncemax-w-0collapses the wrapper, that max keeps the head and drops the tail, so compact and expanded disagree by about one tail and the layout effect flipssetOverflowsuntil React stops at 50 updates.reserveHiddenComposerLabelWidthsums the scrollWidths of the text leaves instead (no element children, non-empty text). Wrappers are skipped, because their scrollWidth shrinks to the clipped box. One-leaf labels (env, workspace, an unsplit branch) reserve the same width as before. A split label reports the same natural width in both states.Checked against the real function with the Chrome widths from a split-under-240 label (
feat/cache-main-20260918-180825, head 143px, tail 62px). The old max differs by 62px and flaps. The sum differs by 0.2px and the compact bit settles. A dev build with that change stayed up on the thread that crashed on2058, with the strip parked compact.- added a commit that references this issue
on Sep 22, 2026 Still present on
0.0.43-nightly.20260922.2096(macOS, Darwin 25.6.0). Three crashes in under four minutes, all on the same persisted thread, identical frames each time:- 2026-09-22T15:04:37.183Z
- 2026-09-22T15:05:56.999Z
- 2026-09-22T15:07:52.587Z
Path:
/1fa31834-63db-4cfe-92d1-cf51722d14db/95f2f53c-e4d5-4098-8c75-825005322039Asset hashes for this build are
dist-15As8Mk0.js/_chat-CmbDh2V8.js. The_chatoffsets moved slightly from the ones reported above (8:59166/8:59847instead of8:59203/8:59884), so the chunk changed between.2071and.2096, but I extracted the bundle fromapp.asarand both frames still land on the same code:setOverflows(...)as the last statement ofmeasure(), called from the dependency-freeuseLayoutEffect. The16hysteresis inBranchToolbar.logicis unchanged."Try again" recovers for a minute or two and then it crashes again on the same thread without any window resize. Reduce Motion is off here, so the animation path is not what keeps it stable or unstable.
T3 Code (Nightly) 0.0.43-nightly.20260922.2096 Error: Minified React error #185 at fi (t3code://app/assets/dist-15As8Mk0.js:26:27482) at li (t3code://app/assets/dist-15As8Mk0.js:26:27007) at Is (t3code://app/assets/dist-15As8Mk0.js:26:58530) at Fs (t3code://app/assets/dist-15As8Mk0.js:26:58152) at t3code://app/assets/_chat-CmbDh2V8.js:8:59166 at t3code://app/assets/_chat-CmbDh2V8.js:8:59847 at Uc (t3code://app/assets/dist-15As8Mk0.js:26:91665) at sl (t3code://app/assets/dist-15As8Mk0.js:26:96136) at xl (t3code://app/assets/dist-15As8Mk0.js:26:104930) at sl (t3code://app/assets/dist-15As8Mk0.js:26:96694)Same crash on Linux, on a newer nightly than the ones reported above:
0.0.43-nightly.20260922.2123(tagd7819c1881). This build is still affected, and nothing merged tomainsince then touchesuseLabelsOverflow/measure().Environment
- T3 Code desktop (AppImage)
0.0.43-nightly.20260922.2123, local server started by the desktop app - Ubuntu 26.04.1 LTS, GNOME on Wayland, x64
- Reduce Motion on: GNOME
org.gnome.desktop.interface enable-animationsisfalse, which Electron reports asprefers-reduced-motion: reduce. With it on, the[overflows]width animation is skipped, so this crash comes from measurement alone. - Window maximized. The desktop trace shows no bounds change between app launch (about 5 hours earlier) and the crash.
What happened
The user was scrolling the timeline or switching threads (unsure which) when the renderer showed the error screen. This is the first time it has happened on this install. The server trace shows the client reading a project'st3.jsonabout 2.5s before the crash, which fits a thread switch. There was no window resize and no renderer restart beforehand;setRendererReadyfires only after the crash, for the error screen and then for "Try again". Other threads on the same server were streaming turns at the time (about 840 SDK messages in the 50s around the crash), which means frequent re-renders.T3 Code (Nightly) 0.0.43-nightly.20260922.2123 Time: 2026-09-23T05:08:01.963Z Error: Minified React error #185 at fi (t3code://app/assets/dist-Cs9Tyim4.js:26:27482) at li (t3code://app/assets/dist-Cs9Tyim4.js:26:27007) at Is (t3code://app/assets/dist-Cs9Tyim4.js:26:58530) at Fs (t3code://app/assets/dist-Cs9Tyim4.js:26:58152) at t3code://app/assets/_chat-CYY5ggvH.js:8:59097 at t3code://app/assets/_chat-CYY5ggvH.js:8:59778 at Uc (t3code://app/assets/dist-Cs9Tyim4.js:26:91665) at sl (t3code://app/assets/dist-Cs9Tyim4.js:26:96136) at xl (t3code://app/assets/dist-Cs9Tyim4.js:26:104930) at sl (t3code://app/assets/dist-Cs9Tyim4.js:26:96694)Stack mapping
I extracted_chat-CYY5ggvH.jsfromapp.asarand checked the frames against the source at the tag. The offsets moved again from.2096(59166/59847→59097/59778), but both frames land on the same code:8:59097issetOverflows(nextOverflows)at the end ofmeasure()inuseLabelsOverflow(apps/web/src/components/BranchToolbar.tsx).8:59778is the layout effect with no dependency array,useLayoutEffect(() => { measure(); }).
No server-side errors near the crash time; the server trace only has unrelated
t3.jsonread misses and "no git remote" warnings.Comment prepared with
t3 triageby Claude Code (Claude Opus 5.5), with input from a second LLM's analysis checked against the logs.- T3 Code desktop (AppImage)
I'm seeing the React #185 error on
0.0.43-nightly.20260924.2187 (78af372cf46a)on macOS 26.7This happens when I scroll up on a brand new thread.
T3 Code (Nightly) 0.0.43-nightly.20260924.2187 Path: /ff879517-756d-4b39-8f07-75b513f341e1/07f885d9-c547-4f63-856e-5f562208ff0e Time: 2026-09-24T05:48:49.054Z Error: Minified React error #185; visit https://react.dev/errors/185 for the full message or use the non-minified dev environment for full errors and additional helpful warnings. at fi (t3code://app/assets/dist-CdJVuaCi.js:26:27482) at li (t3code://app/assets/dist-CdJVuaCi.js:26:27007) at Is (t3code://app/assets/dist-CdJVuaCi.js:26:58530) at Fs (t3code://app/assets/dist-CdJVuaCi.js:26:58152) at t3code://app/assets/_chat-Cs-KPyQM.js:8:59561 at t3code://app/assets/_chat-Cs-KPyQM.js:8:60242 at Uc (t3code://app/assets/dist-CdJVuaCi.js:26:91665) at sl (t3code://app/assets/dist-CdJVuaCi.js:26:96136) at xl (t3code://app/assets/dist-CdJVuaCi.js:26:104930) at sl (t3code://app/assets/dist-CdJVuaCi.js:26:96694)
Before submitting
Area
apps/web
Steps to reproduce
No deterministic repro. The renderer crashed once to the crash screen while a thread was open on
0.0.43-nightly.20260921.2044. Retry recovered and it has not recurred since.Because the stack decodes cleanly, the mechanism is reproducible by reasoning even though the trigger is not: it is the composer context strip's label compaction loop in
BranchToolbar.tsx, not@legendapp/listas in #11308.Expected behavior
The context strip settles on one of the two label states and the renderer stays up.
Actual behavior
React error #185 (
Maximum update depth exceeded).I extracted the client assets out of the installed
app.asarfor this exact build and matched the stack offsets againstorigin/main:dist-BVnGsFIt.js:26:27485/:27010getRootForUpdatedFiber/enqueueConcurrentHookUpdate(thethrow Error(185)site)dist-BVnGsFIt.js:26:58533/:58155dispatchSetStateInternal/dispatchSetState_chat-DOCEAHoD.js:8:59203setOverflows(nextOverflows), the last statement ofmeasure()inBranchToolbar.tsx_chat-DOCEAHoD.js:8:59884measure()insideuseLayoutEffect(() => { measure(); }), the one with no dependency arraydist-BVnGsFIt.js:26:91668/:96139/:104933/:96697So the loop is: every render runs the dependency-free layout effect,
measure()recomputesneededvsavailableand callssetOverflows, and when the two states disagree about each other the flip-flop is synchronous until React gives up at 50 nested updates. The only guard isCONTEXT_STRIP_COMPACT_EXPAND_HYSTERESIS_PX = 16inBranchToolbar.logic.ts:81, which cannot absorb a disagreement larger than 16px.Note that the label width animation is not involved here: this machine has Reduce Motion on, so the
[overflows]layout effect returns at theprefers-reduced-motioncheck before animating anything. The loop is measurement only.Suspect, stated as a hypothesis rather than a measurement: #12805 (
feat(web): truncate branch names and paths in the middle, merged 2026-09-20 23:43 -0300) is in this build. I confirmed it by finding the minifiedsplitForMiddleTruncatein the shipped chunks. That PR changed the branch label thatmeasure()measures, fromto a
MiddleTruncate, which renders two spans (min-w-0 truncatehead plusshrink-0tail) inside aninline-flex overflow-hiddenwrapper. Butmeasure()still reserves hidden text asa max over the subtree, which describes one clipped span, not a head/tail split whose real width is closer to head + tail. The shipped bundle also shows the strip in a mixed state: the branch label is already
MiddleTruncate, while the "Run on" and environment labels still render the old single truncating span, so the two label kinds are now measured by the same formula with different accuracy. Combined with themax-w-[240px]cap on the expanded label andmax-w-0in compact, the reconstructedneededcan differ between the two states by much more than 16px.I have not reproduced the numeric path, so treat the causal link as dated correlation plus a plausible mechanism, not proof.
Impact
Major degradation or frequent failure
Version or commit
T3 Code (Nightly) 0.0.43-nightly.20260921.2044
Environment
macOS 26.6.2 (arm64), MacBook Pro built-in Liquid Retina XDR plus a 5120x2880 external display, DPR 2. Reduce Motion is enabled system-wide. Window size at crash time was not captured. The thread path from the crash screen is omitted because it contains private identifiers.
Logs or stack traces
This is the full stack as shown on the crash screen; it is not truncated.
Workaround
Retry from the crash screen recovered immediately and it has not recurred. Since the decision depends on strip width and on the current branch, model and environment label text, widening the window should move the strip out of the flapping band.