feat(desktop): graceful main-process startup-crash UI (NAN-701)#428
Draft
yagudaev wants to merge 2 commits into
Draft
feat(desktop): graceful main-process startup-crash UI (NAN-701)#428yagudaev wants to merge 2 commits into
yagudaev wants to merge 2 commits into
Conversation
Replace Electron's default uncaught-exception dialog (raw stack-trace modal that gives the user nowhere to go) with a custom dialog that: - Writes the full unsanitized exception to ~/Library/Logs/VoiceClaw/ startup-crash-<ISO>.log so support has the real trace. - Fires a sanitized PostHog `app.startup_failed` event so we get a remote signal even when the user can't read their own logs. - Shows four actionable buttons: Reveal Logs, Copy Diagnostic, Reinstall (opens the latest release), and Quit. - Strips the user's home dir and the `<App>.app/Contents` prefix from anything the user might paste publicly (clipboard + dialog text). - Is idempotent — multiple uncaughtException / unhandledRejection events in sequence still surface only one dialog. A `VOICECLAW_FORCE_CRASH=1 yarn dev` escape hatch throws inside whenReady so the dialog can be exercised end-to-end without breaking real code paths. Adds a Troubleshooting docs section explaining what each button does. Closes NAN-701
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
PR-only artifact so reviewers can see the dialog rendering without running yarn dev with VOICECLAW_FORCE_CRASH=1 themselves.
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.
Why
When the Electron main process throws an uncaught exception during startup, the user gets the platform-default dialog: yellow-triangle modal, a raw stack trace dumping
app.asarpaths, and an OK button that just dismisses the box and leaves the app dead. No log to attach, no telemetry, no recovery path. We saw this in v0.7.0–v0.10.0 with the archiver crash; this ticket replaces the default dialog so any future startup crash gives the user — and us — somewhere to go.Closes NAN-701.
What changed
desktop/src/main/startup-crash.ts— wiresprocess.on('uncaughtException')andprocess.on('unhandledRejection')as the first thing the main process does, before any service spawns.~/Library/Logs/VoiceClaw/startup-crash-<ISO>.logsynchronously, so even if the dialog can't render the support trail still exists.app.startup_failedvia the existing PostHog wrapper witherror_class,error_message,stack_preview,app_version,platform,arch,source. Sanitization strips the user's home dir (/Users/<name>/…→~/…) and any<App>.app/Contentsprefix, and clamps the formatted summary at 500 chars.<class>: <message>plus the top three stack frames go into the dialog'sdetailfield (smaller text under the main message — no separate expand UI; native dialog doesn't support one).Quitis the default + cancel, so Esc / Enter both close cleanly.<log-path>\n\n<sanitized summary>to the clipboard and shows a follow-up confirmation dialog.crashHandledflag means multiple exceptions in sequence still produce one dialog.VOICECLAW_FORCE_CRASH=1 yarn dev— dev-only escape hatch that throws oncewhenReadyresolves, so the dialog can be exercised end-to-end. No effect in packaged builds.docs/src/content/docs/desktop/troubleshooting.mdxdescribing each button.Screenshot
Triggered with
VOICECLAW_FORCE_CRASH=1 yarn dev:Note the path is sanitized to
~/Library/Logs/...and~/code/voiceclaw/...rather than/Users/<name>/....Test evidence
End-to-end exercised with
VOICECLAW_FORCE_CRASH=1 yarn dev:✅ Custom dialog rendered (NOT the raw stack-trace one)
✅ Path in the dialog reads
~/Library/Logs/VoiceClaw/startup-crash-2026-05-10T18-48-08-577Z.log(sanitized)✅ Crash log file appeared on disk with full unsanitized stack:
✅ Buttons: Reveal Logs · Copy Diagnostic · Reinstall · Quit (Quit default)
✅ Sanitization confirmed in dialog text + clipboard payload (no
/Users/<actual-name>/…, no/Applications/<App>.app/Contents/…)Unit tests
desktop/src/main/startup-crash.test.ts— 12 tests, all passing alongside the existing 127:sanitizeStartupErrorstrips~,<app>, caps at 5 frames, truncates at 500 chars, preserves error classbuildClipboardPayloadputs the log path on line 1 and the sanitized summary belowhandleStartupCrashwrites log, fires telemetry, shows dialog, exits 1handleStartupCrashis idempotent across repeated callsTest plan
yarn typecheck— cleanyarn test— 139/139 passVOICECLAW_FORCE_CRASH=1 yarn devshows the new dialogapp.startup_failedevent lands in PostHog after a real test crashOut of scope (per ticket)
🤖 Generated with Claude Code