feat: surface editor crashes natively and offer a reload - #642
Draft
dcalhoun wants to merge 2 commits into
Draft
Conversation
A crashed editor left Gutenberg's own fallback on screen — a desktop notice offering to copy the post contents and a stack trace. Neither helps here: the copy button reads the post through `getEditedPostContent()` at click time, and the boundary sits above `EditorProvider`, whose unmount clears the post pointer, so it always copies an empty string. Cover the editor with a `ContentUnavailableView`, matching the existing `displayError` treatment for load failures, and offer `reloadEditor()`. The reloaded editor starts from whatever the host returns from `editorDidRequestLatestContent`, so work up to the host's last autosave survives, and readiness is restored only once `onEditorLoaded` arrives again. The copy button is dropped rather than kept alongside the native UI, since it never worked. The description deliberately makes no claim about saved work. Whether anything was persisted is the host's business — some mirror content continuously, others read it only when the user saves — so a host that does mirror should override that key to reassure the user. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VuxMbKtUsaUF8nUVgKdxwK
Mirrors the iOS treatment: cover the editor with a notice offering to reload, rather than leaving Gutenberg's desktop fallback on screen. `EditorErrorView` gains an optional action button so the crash state can offer `reloadEditor()`, which clears `didFireEditorLoaded` so the reloaded editor re-announces itself, and returns to the spinner until it does. The description deliberately makes no claim about saved work, since whether anything was persisted is the host's business. Robolectric needs the merged resources to construct the error view now that it reads a string resource, so unit tests opt into Android resources and `GutenbergViewTest` drops `manifest = Config.NONE`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VuxMbKtUsaUF8nUVgKdxwK
dcalhoun
added this pull request to stack #641
September 9, 2026 16:33
XCFramework BuildThis PR's XCFramework is available for testing. Add the following to your .package(url: "https://github.com/wordpress-mobile/GutenbergKit", branch: "pr-build/642")Built from 6ccfb7b |
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.
What?
A crashed editor left Gutenberg's own error fallback on screen, and the user had no way back other than closing the post. GutenbergKit now covers the editor with a native notice and offers to reload.
Why?
Fix the second half of CMM-2008. #638 and #640 stop the bridge failures; this makes the resulting state legible and recoverable.
Gutenberg's fallback is built for the desktop editor and neither of its actions helps on a phone:
getEditedPostContent()at click time, but the boundary sits aboveEditorProvider, whose unmount runssetEditedPost( null, null ). It always copies an empty string.GutenbergKit already owns the equivalent UI for the states either side of this one —
displayError/EditorErrorViewfor load failures, progress and spinner views for loading.GutenbergView's own documentation states the contract: "This view manages its own loading UI internally… Consumers do not need to implement loading UI." A crash is the same kind of state, so it belongs here rather than being rebuilt in every host.Native rather than web, specifically: the web fallback is rendered by a React tree that just crashed, and anything built there has to survive the same unmount that broke the copy button. The native layer is healthy by definition and, after #638/#640, knows authoritatively.
How?
ContentUnavailableViewcovering the editor, matchingdisplayError, plus a publicreloadEditor().EditorErrorView, which gains an optional action button.reloadEditor()clearsdidFireEditorLoadedso the reloaded editor re-announces itself, and returns to the spinner until it does.canCopyContent, removing the button that never worked.Reload restores content from the host's
editorDidRequestLatestContent/LatestContentProvider, so work up to the host's last autosave survives. Readiness is restored only whenonEditorLoadedarrives again, so bridge calls stay refused until the editor is genuinely usable.The description makes no promise about saved work
The copy reads "Reload the editor to continue editing." and deliberately stops there.
Whether anything was persisted is entirely the host's business. WordPress-iOS mirrors content to Core Data about a second after typing stops;
CustomPostEditorViewControllerand the comment editor have no mirror at all and read content only when the user saves. GutenbergKit cannot tell which it is embedded in, so it must not claim the work is safe — on the unmirrored hosts that would be false, and would encourage someone to close a post and lose the lot.Hosts that do mirror content should override
editorCrashedDescription(iOS) /gbk_editor_crashed_description(Android) to say so. Both are annotated with why.Reload is user-initiated, not automatic
An automatic retry would recover transient crashes invisibly, but risks a loop when the crash comes from the post's own content, and silently discards anything since the last autosave. An explicit button keeps the user in control and matches how
displayErroralready behaves. Worth revisiting if telemetry shows most crashes are transient.Note for review
GutenbergViewTestdropsmanifest = Config.NONEand the module opts intoisIncludeAndroidResources, because Robolectric needs the merged resources to construct the error view now that it reads a string resource. The pre-existingHttpServerAuthenticationTestsfailures are unrelated — they fail identically on cleantrunkhere (a local proxy returning 407).Testing Instructions
Trip the boundary from the web inspector (iOS) or
chrome://inspect(Android), on a post with at least one block:Unit tests:
./android/gradlew -p ./android :Gutenberg:testDebugUnitTest --tests "org.wordpress.gutenberg.GutenbergViewTest"Accessibility Testing Instructions
The crash view is standard
ContentUnavailableView/TextView+Buttoncontent, so VoiceOver and TalkBack read the title, description, and button label, and the button is reachable by keyboard and switch control. Worth confirming the reload button is announced with its label and that focus moves into the notice when it appears.Screenshots or screencast
To add — before/after of the crash state on both platforms.
🤖 Generated with Claude Code
https://claude.ai/code/session_01VuxMbKtUsaUF8nUVgKdxwK