Skip to content

fix(muya): guard stale cursor/block lookups against null#4267

Merged
Jocs merged 1 commit into
developfrom
fix/muya-null-block-guards
May 23, 2026
Merged

fix(muya): guard stale cursor/block lookups against null#4267
Jocs merged 1 commit into
developfrom
fix/muya-null-block-guards

Conversation

@Jocs
Copy link
Copy Markdown
Member

@Jocs Jocs commented May 23, 2026

Summary

The Muya editor's click / keyboard / backspace handlers re-dereference cursor state that can outlive the render which removed the block it points to (code-block reload, image figure swap, async rerenders, etc.). getBlock() then returns null and the renderer crashes with Cannot read properties of null (reading 'text').

This PR adds null guards at four call sites — three on the actual code paths reported in the issue tracker, plus one same-family hardening in selectionChange to protect getTOC-style callers.

  • checkNeedRender (updateCtrl.js): bail out when start/end block is missing
  • clickHandler (clickCtrl.js): guard the format-picker branch on the same lookup
  • deleteImage (imageCtrl.js): drop the stale selection + close image toolbar instead of reading .text on null
  • selectionChange (paragraphCtrl.js): return an empty affiliation when blocks are missing

Issues closed

checkNeedRender family:

deleteImage family:

getTOC / selectionChange family (official code-path hardening):

Related (already closed as duplicates)

Test plan

  • pnpm run typecheck — clean
  • pnpm exec eslint on the four touched files — clean
  • pnpm run test:unit — 523 tests pass (2 pre-existing suite-load failures unrelated to this change)
  • Manual: place cursor in a code block, swap files, click back — no renderer crash
  • Manual: select an inline image, press Backspace twice — second press no-ops instead of crashing

🤖 Generated with Claude Code

Cursor state can survive a render that removed the block it references
(code-block reload, image figure swap, async rerenders, etc.). The
follow-up click / keyboard / backspace handler then dereferenced the
null result from getBlock() and crashed the renderer.

- checkNeedRender: bail out when start/end block is missing
  (fixes #3674, #3842, #4032, #4072, #4122, #4143, #4162)
- clickHandler: guard the format-picker branch on the same lookup
- deleteImage: drop the stale selection instead of reading .text on null
  (fixes #3950)
- selectionChange: return an empty affiliation when blocks are missing,
  protecting getTOC-style callers (same family as #3999)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 23, 2026 07:03
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens Muya editor event handlers against stale cursor state where getBlock(key) can legitimately return null after a prior render removed or replaced the referenced block, preventing renderer crashes like Cannot read properties of null (reading 'text').

Changes:

  • Add null-guards in checkNeedRender and clickHandler to avoid dereferencing missing blocks during render/format-picker logic.
  • Harden deleteImage to clear stale image selection and hide related UI instead of reading .text on a missing block.
  • Make selectionChange return an empty affiliation when cursor block lookups fail, avoiding downstream crashes for selection-based callers.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/muya/lib/contentState/updateCtrl.js Early-return from checkNeedRender when start/end blocks can’t be found, preventing .text deref crashes.
src/muya/lib/contentState/clickCtrl.js Guard format-picker condition against block being null when resolving selection keys.
src/muya/lib/contentState/imageCtrl.js Handle missing image block by clearing selectedImage and closing toolbars/transformer safely.
src/muya/lib/contentState/paragraphCtrl.js Return { affiliation: [] } when selection blocks are missing, avoiding null.type crashes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@github-actions
Copy link
Copy Markdown

Build artifacts for PR #4267:

Run: https://github.com/marktext/marktext/actions/runs/26326448643

Artifact Size Link
marktext-linux 594.5 MB Download
marktext-macos-x64 534.9 MB Download
marktext-windows 272.8 MB Download
marktext-macos-arm64 534.6 MB Download

@Jocs Jocs merged commit 70e43b9 into develop May 23, 2026
10 of 11 checks passed
@Jocs Jocs deleted the fix/muya-null-block-guards branch May 23, 2026 07:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment