Repository navigation
Conversation
OrcaWin
force-pushed
the
crash/s46-chat-editor-focus-before-mount
branch
from
October 23, 2026 00:08
a609728 to
252c8b9
Compare
Collaborator
Author
|
Crash scan 52 found another sighting on v1.4.240 (report b40f5f19, macOS 27 arm64, boundary |
added 2 commits
October 26, 2026 07:04
…estroyed prompt editor A Suspense retry commits up to 300ms after it renders. useEditor destroys an editor whose mount effect has not run 1ms after creating it, so that commit hands the prompt handle a destroyed editor, and the new-chat focus claim (a layout effect) threw "The editor view is not available. Cannot access view['focus']". The handle now reports a destroyed editor as disabled and skips view reads and edits on it; the next render swaps in the fresh editor and the reveal-focus frame retry focuses it.
OrcaWin
force-pushed
the
crash/s46-chat-editor-focus-before-mount
branch
from
October 26, 2026 14:08
252c8b9 to
ff6173d
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
ELI5
The chat box at the bottom of an agent chat is built from a text-editor widget. When a new chat opened, Orca tried to put the cursor in that box right away. Sometimes the box was shown a little late (React waits up to about 300ms before revealing a loading section), and by then the editor library had already thrown the unused editor away. Orca then told the thrown-away editor to take focus, the editor library threw an error, and the whole tab fell into the error screen. Now Orca checks first: if the editor has been thrown away, it skips that one attempt. The focus logic already retries on the next few frames, and by then a fresh editor is in place, so the cursor still ends up in the chat box.
The same thrown-away editor could also crash a different way: when one chat box tried to grab the cursor, Orca asked every other chat box on screen "is the cursor inside you?", and asking a thrown-away editor that question threw the same error. The same check now answers "no" for a thrown-away editor, so that path is covered too.
What Changed
Before: opening a new chat or agent session could sometimes replace the tab with the error screen ("Something went wrong"). This shows up in crash report
4f1f0de8on 1.4.237:Crash report
55eb9500on 1.4.237 (found in crash Scan-48) hits the same destroyed editor through a sibling composer:After: the new chat opens normally, and the cursor lands in the composer.
Mechanism (
src/renderer/src/components/native-chat/NativeChatPromptEditor.tsx):useEditordestroys any editor whose mount effect has not run within 1ms of the editor being created. React can delay committing a Suspense reveal by up to about 300ms, so the composer's handle can be attached to an editor that is already destroyed. In the same commit, the new-chat reveal-focus layout effect callsfocus(), and that readseditor.view. On a destroyed editor,editor.viewis a stub that throws on most keys.liveView(editor)returnsnullwhen the editor is destroyed.focus,containsandisCaretOnVisualEdgego through it and do nothing (or return false) when no view exists.disabledgetter now returnseditor.isDestroyed || !editor.isEditable. Before, it reported a dead editor as usable, because the stub sayseditable: true. With this change the composer wrapper'sfocus()returnsfalse, so the existing reveal-focus retry loop (6 frames) tries again and focuses the replacement editor thatuseEditorbuilds on the next render.insertText,insertSkill,removeImageReferencesandinsertFileReferencesreturn early on a destroyed editor, because they would otherwise throw a different error about a null schema or command manager.Why
The crash is a timing gap between the editor library's 1ms destroy timer and React's delayed Suspense commit. Neither side is wrong by its own rules. The narrowest fix is to make the prompt handle honest about a dead editor, which follows the existing
!editor.isDestroyedguards incomponents/editor. Options I rejected:Scan-54 update (2026-10-26, rebased on main 8851890)
Three more of these crashes on 1.4.241 (the current release) in one day, on macOS and Windows:
86cba930,68477af0,ef36bda1. The stack is identical: reveal-focus reclaim, then composerfocus(), theneditor.view.focus(), then[tiptap error] ... Cannot access view['focus'], caught byterminal.workbench.focuscomment. I kept main's wording and kept theliveViewguard.NativeChatPromptEditor.tsxtoorigin/mainand both delayed-commit tests fail with the production error[tiptap error]: The editor view is not available. Cannot access view['focus']. With this branch, 2/2 pass.pnpm tc:webpasses. The native-chat suite passes (381 files, 4268 tests). oxlint, oxfmt andcheck:code-quality:changedare clean.focus,domandendOfTextblockthrow, and all three go throughliveView. The retry lands on the replacement editor.Scan-49 update (re-verified on current main, no code change)
Description
Orca crash report (Slack #crash channel), Orca 1.4.237: the same
[tiptap error]: The editor view is not available. Cannot access view['focus']crash in an agent-session tab, about 18s after start, caught by theterminal.workbencherror boundary.Root cause (unchanged): the reveal-focus hook calls the composer handle's
focus()synchronously in auseLayoutEffectoncecomposerReadyis set and a focus intent is pending for the tab. A Suspense reveal can delay the commit (throttled to about 300ms).useEditordestroys an editor whose mount effect did not run within 1ms of creation. So the commit gives the imperative handle a destroyed editor whoseeditorViewis null, andeditor.view.focus()hits tiptap's view proxy, which throws.Frame mapping from the extracted v1.4.237 arm64
app.asar. The released sourcemaps zip has no renderer maps, so the frames were mapped by reading the minified code:OnboardingInlineCommandTerminal-C2cRmP37.js:131:1263isfocus:()=>{d.view.focus(),d.isEditable||d.view.dom.focus()}, the handlefocus()inNativeChatPromptEditor.tsx.:132:59097isfocus:(0,Q.useCallback)(()=>{let e=s();return e?.focus(),e!==null},[s]), thefocuscallback inuse-native-chat-typed-insertion.ts.client-B0x6L90P.js:73:188558/188640is the reveal-focususeLayoutEffectr(), called throughNJ?.tabId===e&&(NJ.cancel(),r()). That isuse-native-chat-composer-reveal-focus.ts:109-122.None of these files (
NativeChatPromptEditor.tsx,use-native-chat-composer-reveal-focus.ts,use-native-chat-typed-insertion.ts) changed on origin/main since the PR basef0450469e42, so main still has the crash.Fix evidence
Run in the PR worktree at head
252c8b9e95a:git merge-tree --write-tree origin/main HEADexits 0, so the PR still merges cleanly onto current main (5d32f372e76). No rebase was needed, and nothing new was pushed.In tiptap core 3.31.3,
isDestroyedreturnsthis.editorView?.isDestroyed ?? true. It is true for both the never-mounted and the destroyed case, so theliveView(editor)guard covers this report.ELI5
The chat box's text editor sometimes gets thrown away and rebuilt when the screen shows up a little late. The app tried to put the cursor into the old editor after it was thrown away, and that crashed the tab. The fix first checks whether the editor is still alive. If it isn't, the app skips the focus, and the fresh editor that appears a moment later gets focus normally.
Trade-offs
No new trade-offs since Scan-48. When the handle still points at a destroyed editor,
focus,insertandcontainscalls do nothing instead of throwing. On the rare delayed-reveal path, an initial auto-focus or a typed or pasted insertion aimed at the dead editor is dropped instead of crashing. The existing reclaim pass then focuses the re-rendered live editor, and the PR's tests cover that.Adversarial review
Scan-49: 1 loop, final CLEAN.
EditorInstanceManager.scheduleDestroydestroys the editor 1ms after creation unless its effect has run. The destroyededitor.viewproxy then throws onfocusanddom.disabledreturns true for a destroyed editor, so the wrapper'sfocus()returns false and the claim is retried for up to 6 frames. MeanwhilerefreshEditorInstancecreates a fresh editor, and a later retry focuses it.editorViewis null, never on a live editor. The unguarded paths only touch proxy keys that don't throw.pnpm tc:webclean,oxlintclean on the changed files.acceptsTextandfocusreport the composer as unavailable. A draft written to the dead editor during that window is not carried over to the fresh editor. This already happens on main and is not introduced by this PR.Linked crash reports
No issue.
4f1f0de8, Orca 1.4.237:Cannot access view['focus']viareclaim->focus(found in crash Scan-46).55eb9500, Orca 1.4.237:Cannot access view['dom']viareclaim->Array.some->containson a sibling composer (found in crash Scan-48). No new production change was needed: theliveView()guard incontains()already covers it, and so does theeditingSiblingComposercheck inclaim(), which uses the samecontains. This update adds a test that proves it.Fix evidence
The Scan-48 update adds a second real-code test to
NativeChatPromptEditor.delayed-commit.test.tsx:reclaims focus past a sibling composer that committed a destroyed editor. It renders two real composers (realNativeChatPromptEditor+useNativeChatTypedInsertion+useNativeChatComposerRevealFocus, no mocks) inside a Suspense boundary whose lazy sibling resolves after 10ms. React's 300ms retry throttle delays the commit, souseEditorhas already destroyed both editors. A pending focus request for the second composer's tab runsreclaim(), and themountedComposers.some(... contains(active))check reaches the first composer's dead editor.With origin/main's
NativeChatPromptEditor.tsxswapped in (git show origin/main:<file> > <file>), verbatim:The sibling test failed 4 of 4 runs on main, with the exact message and call path from report
55eb9500.With this PR's
NativeChatPromptEditor.tsx(liveViewguard), verbatim:This passed 3 of 3 runs. The sibling test also asserts that
document.activeElementis the reclaiming composer's textbox.Test isolation note: the first draft of the sibling test passed falsely on main when it ran after the focus test. happy-dom left
document.activeElementon the previous test's detached textbox, sowaitForfinished before anything committed. Instrumenting showed that neithercontainsnorfocuswas called. The focus check is now scoped to the test's own container.Visual Proof
N/A. This prevents an error screen in a timing window that is hard to trigger by hand. The proof is the automated test below: it fails with the exact production error before the fix and passes after.
Testing
Scan-48 update: the branch is rebased onto origin/main (it was 193 commits behind) and force-pushed with lease. Head is
252c8b9e95a. Native-chat suite:Test Files 384 passed / Tests 4205 passed.pnpm tc:web:No type errors in 15,625 files.oxlinton both changed files: clean. The pre-commit oxlint, react-doctor and oxfmt hooks also passed.Original fix evidence (Scan-46). The new test
src/renderer/src/components/native-chat/NativeChatPromptEditor.delayed-commit.test.tsxuses only real code. It rendersNativeChatPromptEditor,useNativeChatTypedInsertionanduseNativeChatComposerRevealFocuswith a pending new-chat focus request, inside a Suspense boundary held by a lazy sibling. Nothing is mocked to throw. React's own Suspense throttle creates the delayed commit.Before the fix (verbatim):
After the fix (verbatim, 5 of 5 runs passed):
The test asserts that no uncaught error occurred and that
document.activeElementis inside the composer, so focus really lands.Revert check: with the fix removed, the test failed 3 of 3 runs with the same
Cannot access view['focus']error. A separate review pass that swapped main'sNativeChatPromptEditor.tsxback in got the same result.Other checks:
Test Files 370 passed (370) / Tests 4018 passed (4018)pnpm tc:web:No type errors in 15,445 filesoxlinton changed files: cleanpnpm run check:code-quality:changed: passed, 0 new findingsPlatforms: macOS (unit tests). The change is renderer-only and does not depend on the platform. It does not touch SSH or remote paths.
Trade-offs
isCaretOnVisualEdgereturns false when no view exists, so arrow-key prompt recall can't fire then. That can only happen before the view exists.contains = falseand does not take focus until the next render swaps in a live editor.Adversarial review
Scan-48 update (sibling-composer test): 1 loop, final CLEAN.
Original fix (Scan-46): 1 loop, clean = true.
useEditorhas already destroyed, and the view stub throws onfocus). It also confirmed thatdisablednow makes the composer wrapper'sfocus()return false, so the 6-frame reveal-focus retry focuses the replacement editor. No new deferred work was added, so focus can't land on the wrong tab later. The reviewer swapped main's file back in, got the exact crash message, then restored the file. The reviewer re-ran the prompt-editor tests (17 passed),tc:web, the code-quality gate and oxlint, all clean. One minor non-blocking note on the command paths.Agent skill upstream boundary
Notes
Ensure no issues in: Security, Cross-platoform support (Linux, Windows, Mac), Remote SSH, Mobile, general backwards compatibility, performance
Renderer-only guard: no wire, IPC or storage changes, no platform-specific code, and no performance cost (one boolean check per handle call).
Checklist
N/Awith reasonpnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)