Skip to content

fix(spell-check): stop service views crashing while typing - #723

Merged
manusa merged 2 commits into
mainfrom
bug-while-typing
Sep 14, 2026
Merged

manusa merged 2 commits into
mainfrom
bug-while-typing

Conversation

@manusa

@manusa manusa commented Sep 14, 2026

Copy link
Copy Markdown
Owner

Problem

Since v0.0.123, typing in a service (a Slack or Telegram DM, for example) eventually kills the service view and leaves a blank screen. It only happens with the non-native spell checker (the nodehun dictionaries).

The renderer segfaults with EXC_BAD_ACCESS at 0x18 on its main thread, from a resolved promise calling back into Electron.

Root cause

  • Electron's SpellCheckClient tracks a single pending request. OnSpellCheckDone dereferences it without a null check: pending_request_param_->wordlist(), which sits at offset 0x18.
  • Chromium 152, which came with the Electron 39 → 44 bump, added user-dictionary-leak mitigations to IdleSpellCheckController. A selection change in an element that wasn't focused by a user gesture, or a content change without transient user activation, now calls Deactivate(). That cancels Blink's in-flight check. Slack and Telegram focus their composers by script.
  • Blink then issues a new request on the next keystroke. The late answer to the cancelled request completes the new one and clears the pending pointer. The new request's own answer then finds nothing pending, and the renderer crashes.
  • Chromium 142 only deactivated when spell checking was disabled or the document was destroyed, which is why this never showed up before.

Fix

preload.spell-check.js now only answers the most recent spellCheck request, exactly once, and delivers the answer from a requestIdleCallback.

The idle hop covers the case where Blink has issued a request that hasn't reached the preload yet. Electron hands requests over from a normal-priority task it posts, and that task always runs before an idle callback, so the stale answer finds itself superseded.

A dictionary failure still answers with nothing misspelled, so Blink never stalls on an unanswered request.

Testing

  • preload.spell-check.test.js now covers a request superseded while its dictionary lookup is in flight, and one superseded while its answer waits for the renderer to be idle. Removing only the superseded-request guard makes exactly those two tests fail.
  • npm run pretest and npm test pass (60 suites, 1181 tests).
  • A standalone Electron app with scripted typing into a script-focused contenteditable reproduces the crash deterministically:
Setup Result
Electron 44.3.0, previous provider crashed 3/3 at the first overlapping request
Electron 39.1.2, previous provider survived 3/3, no overlapping requests
Electron 44.3.0 with UnrestrictSpellingAndGrammarForTesting survived 3/3, no overlapping requests
Electron 44.3.0, this fix survived 3/3 (21 overlapping requests, 20 stale answers dropped)

Enabling the native spell checker is a workaround until this ships.

Since Electron 44 (Chromium 152), Blink cancels an in-flight spell
check when a script moves the caret in a field the user didn't focus,
as Slack and Telegram composers do. The late answer to the cancelled
request completes the newer one in Electron's SpellCheckClient, whose
own answer then dereferences a null pending request and segfaults the
renderer, leaving a blank service view.

Answer only the most recent request, and only once the renderer is
idle, so a request Electron has accepted but not yet handed over
always supersedes a stale answer before it is delivered.

Signed-off-by: Marc Nuri <marc@marcnuri.com>
A timed-out idle callback runs as a regular task, which could beat
Electron's hand-over of a newer spell check request and reopen the
renderer crash. State the requirement next to the delivery and fail a
test if a timeout is ever passed.

Signed-off-by: Marc Nuri <marc@marcnuri.com>
@sonarqubecloud

Copy link
Copy Markdown

@manusa
manusa merged commit e31bab1 into main Sep 14, 2026
6 checks passed
@manusa manusa added this to the 0.1.0 milestone Sep 14, 2026
@manusa
manusa deleted the bug-while-typing branch September 14, 2026 13:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant