Libra CodeHub

CedarCopilot/cedar-mail

Branch: staging

fix(mail): stop a destroyed editor reaching onEditorCreated and killing the page

merged#2486CedarCopilot

CedarCopilot wants to merge 0 commits into staging from fix/destroyed-editor-on-editor-created

Live on prod, no production signal yetTimeline and evidence
  1. Opened
    Aug 14, 2026, 2:02 PM
  2. Merged
    Aug 14, 2026, 2:11 PM
  3. Live on staging
    Aug 14, 2026, 2:11 PM
  4. Live on prod
    Aug 14, 2026, 2:11 PM
  5. Observed 5 hours
    Aug 14, 2026, 2:11 PM
  6. Unobserved

    Live on prod, no production signal yet

    Aug 14, 2026, 2:11 PM
  7. Pipelines steady after this deploy
    Aug 14, 2026, 2:11 PM
  8. Pipelines steady after this deploy
    Aug 14, 2026, 2:11 PM

Behaviors Libra is checking

Email workflow behavior from components/markdown-editor.tsx keeps working in staging.Inconclusivelow confidence

Strict CloudWatch fallback saw 2,254 success-shaped log lines matching email, emails, gmail, mail, inbox, but no tied operation was present, so Libra is not calling this working.

staging, checked Aug 17, 2026, 9:27 AM
Email workflow behavior from components/markdown-editor.tsx keeps working in prod.Not checked
prod

Failures attributed to this change

Customer impact is not proven from the retained evidence. 6 hits · 1 user · no retained trace sample.Introducedmedium confidence

single_user

prod, first seen Aug 14, 2026, 2:56 PM
Customer impact is not proven from the retained evidence. 1 hit · no retained affected-user count · no retained trace sample.Introducedmedium confidence

single_user

prod, first seen Aug 14, 2026, 2:56 PM
Customer impact is not proven from the retained evidence. 1 hit · no retained affected-user count · no retained trace sample.Introducedmedium confidence

single_user

prod, first seen Aug 14, 2026, 2:56 PM

Libra has verdicts on 0 of 1 tracked behaviors on prod; 1 is still being checked. Libra attributed 3 failures to this change at medium confidence and is still confirming them. Libra checks hourly for 3 days after each deploy.

What was wrong

Four users hit the route error boundary , the full-page "Something went wrong!" , today: Zach Moskow (concentrate.ai) at 19:47/19:52 UTC, Trey Layton (Pax AI) at 20:13, plus Keenan Worthington (Pirros) and Sam MacGregor (Simcare) earlier.

Every event is the same exception, Cannot read properties of null (reading 'commands'), with resolved frames onEditorCreated → TipTap's get commands, on /agent?conversationId=…&chat=… and /home?chat=… URLs.

MarkdownEditor announces its editor to the parent from a useEffect on [editor], gated by if (editor) , markdown-editor.tsx:236. That is exactly the half-guard 0bcd5cde0 was about: TipTap builds the editor a tick after mount and destroys it on a 1ms timer after unmount, and a destroyed editor is not null , it keeps its identity while commandManager/view/state are nulled underneath it. A surface that disappears in that gap leaves the effect holding a live-looking editor, and every consumer callback reaches for .commands or .getMarkdown() first thing. Because the throw lands inside an effect, React hands it to the route error boundary and the whole page is replaced.

0bcd5cde0 guarded the effect sites that touch an editor and shipped with today's 19:40 UTC deploy , verified live, read-only-markdown-view now compiles to liveEditor(editor)?.commands.setContent(…). It left the handoff itself unguarded, so the deployed bundle (release 7ddb16f8) still carries two unguarded callbacks in the ConversationView chunk:

  • NextStepsCard , editor.commands.setContent(nextSteps, …) (the byte at line 31, col 3339 of the live ConversationView-Ccl09jSG.js, i.e. the exact throwing site)
  • the Slack composer in UniversalComposer , setContent / getMarkdown

Zach's and Trey's errors are on ConversationView-Ccl09jSG.js, the post-deploy build. This path was still open.

Why it started. The seeding code is unchanged since 2026-04-23, the handoff since 2026-03-29, and TipTap's destroy machinery is byte-identical between 3.18.0 and 3.26.1 (I diffed them , the June upgrade is not the cause). What changed is where the card lives: b6ded45e9 put NextStepsCard on the Overview tab (2026-07-13) and 47dc63f32 gave that tab doc embeds (2026-07-27), so an artifact now slides over it and unmounts it mid-creation. The first page-replacing occurrence is 2026-07-29 17:00 UTC, on a /strategicOverview/ URL.

What changed

One guard, at the handoff rather than at the seven callbacks that consume it:

const live = liveEditor(editor);
if (live) onEditorCreated?.(live);

Guarding here means no consumer can ever be handed a dead editor , including the four outside the conversation view (AgentsEditor, the share page, the agents demo) that would each otherwise need the same line. Nothing is lost by skipping the call: a destroyed editor cannot accept content, and a real remount builds a new editor, which changes the [editor] dep and re-runs the effect to seed normally.

How to test

apps/mail/tests/components/markdown-editor-liveness.test.tsx drives useEditor directly so the destroyed state is exact rather than timing-dependent. Three cases: a destroyed editor is never announced, a live one still is and seeding works, and null announces nothing.

pnpm --filter @zero/mail exec jest tests/components/markdown-editor-liveness.test.tsx

Verified both ways , the first case fails on the old if (editor) line with the production error text, and all three pass with the guard. types:test is clean for both touched files (the remaining errors are pre-existing on staging); eslint is clean.

After deploy, the check is that the Cannot read properties of null (reading 'commands') issue stops taking new events with an onEditorCreated frame.

🤖 Fixed by Sherlock

Greptile Summary

The

Show production surfaces and changed-file mapping

Production surfaces

Libra has not measured any production surfaces for this change yet.

Changed files → surfaces

  • apps/mail/components/markdown-editor.tsxno production surface mapped
  • apps/mail/tests/components/markdown-editor-liveness.test.tsxno production surface mapped