Skip to content

fix(editor): wrap dispatchCommand in editor.update() to prevent read-only errors#129

Merged
zacharias-ona merged 1 commit into
mainfrom
fix/sentry-memo-5-read-only-link-editor
Apr 16, 2026
Merged

fix(editor): wrap dispatchCommand in editor.update() to prevent read-only errors#129
zacharias-ona merged 1 commit into
mainfrom
fix/sentry-memo-5-read-only-link-editor

Conversation

@zacharias-ona
Copy link
Copy Markdown
Collaborator

Summary

Fixes Error: Cannot use method in read-only mode. thrown when saving/removing links or formatting text via the floating toolbar.

Sentry issue: https://ona-2j.sentry.io/issues/MEMO-5 (4 events, 3 users)

Closes #128

Root Cause

editor.dispatchCommand() triggers command listeners synchronously via triggerCommandListenersupdateEditorSync. When activeEditor === editor (true during an editorState.read() callback from the update listener), updateEditorSync takes a fast path that calls the listener directly without creating a writable context. The LinkPlugin listener calls $toggleLinksplitTexterrorOnReadOnly.

Both floating-link-editor-plugin.tsx and floating-toolbar-plugin.tsx called editor.dispatchCommand() from React event handlers without wrapping in editor.update().

Changes

  • floating-link-editor-plugin.tsx: Wrap handleSave and handleRemove dispatchCommand calls in editor.update()
  • floating-toolbar-plugin.tsx: Wrap all format and link toggle dispatchCommand calls in editor.update()
  • lexical-dispatch-safety.test.ts: Static analysis test that scans editor files for bare dispatchCommand() calls with mutating commands outside editor.update()
  • .agents/conventions.md: Document the "dispatching mutating commands" pattern to prevent recurrence

…only errors

Lexical's dispatchCommand() triggers command listeners synchronously. When
called from a React event handler while an editorState.read() is active on
the call stack, mutations (e.g. $toggleLink → splitText) execute in a
read-only context, throwing 'Cannot use method in read-only mode.'

Wrap all mutating dispatchCommand calls in editor.update() in both
floating-link-editor-plugin.tsx and floating-toolbar-plugin.tsx.

Add a static analysis test to prevent regressions and document the pattern
in .agents/conventions.md.

Closes #128

Co-authored-by: Ona <no-reply@ona.com>
@zacharias-ona zacharias-ona added the bug Something isn't working label Apr 16, 2026
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 16, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
memo Ready Ready Preview, Comment Apr 16, 2026 11:09pm

Request Review

@zacharias-ona zacharias-ona merged commit 0e5cdf5 into main Apr 16, 2026
6 checks passed
@zacharias-ona zacharias-ona deleted the fix/sentry-memo-5-read-only-link-editor branch April 16, 2026 23:12
@zacharias-ona
Copy link
Copy Markdown
Collaborator Author

Reviewed and merged. Fix correctly wraps all mutating dispatchCommand calls in editor.update(), static analysis test prevents regression, and convention is documented. Issue #128 marked as done.

@zacharias-ona
Copy link
Copy Markdown
Collaborator Author

✅ UI verification passed — design spec compliance confirmed.

Static analysis: No visual changes in this PR. All modifications are behavioral (wrapping editor.dispatchCommand() in editor.update()). Existing styling in both floating-link-editor-plugin.tsx and floating-toolbar-plugin.tsx remains compliant with .agents/design.md — color tokens, typography, spacing, accessibility attributes, touch targets, and dark mode usage are all correct.

Visual verification: Playwright screenshots of the editor page (desktop dark + mobile) show no regressions. Layout, spacing, and component rendering are intact.

@zacharias-ona
Copy link
Copy Markdown
Collaborator Author

✅ Post-merge verification passed. The changes in this PR (editor dispatchCommand wrapping) do not cause any new regressions.

E2E suite: 35/42 passed, 4 failed, 3 skipped (dependent on a failed test)

Ad-hoc smoke tests: All passed

  • ✅ Landing page — loads, has title
  • ✅ Sign-in page — renders email input
  • ✅ Health endpoint — returns healthy status
  • ✅ Authenticated flow — login, workspace load, editor navigation

Pre-existing failures (not caused by this PR):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: Lexical "Cannot use method in read-only mode" in FloatingLinkEditorPlugin (Sentry MEMO-5)

1 participant