fix: tell the user where crash details are, and stop a fatal crash from being silent - #1708
Merged
Merged
Conversation
…om being silent Closes the crash-reporting gap in #1651, plus the leftover placeholder screenshot in #1659. Both verified against current source first. The dialog. An unhandled dispatcher exception showed Exception.Message alone — for the commonest fault that reads "Object reference not set to an instance of an object.", which the target persona can do nothing with, and which never mentions that a log exists or where it is. The app knew exactly where the evidence went and did not say, so the report arrives as "it just closed" with nothing attached. The message now states what happened, that the app is still running (OnUi sets e.Handled = true and continues, so implying otherwise would push the user into force-closing a working app), and the exact folder. A wrapper exception's generic text is skipped in favour of its inner message, and a blank message falls back to something readable rather than an empty dialog. Built from string literals, the exception text, and one static property — deliberately nothing that touches DI, the theme, or any service, since any of those may be the component that just faulted. The existing MessageBox stays for the same reason, as its comment already argues. The silent death. OnDomain logged the exception and let the process die with no UI at all: the window vanished and nothing on the next start said the previous session had ended badly. It now writes a marker, and Dashboard's init surfaces it once — a toast, not a dialog, because this is information and a modal blocking every launch after one crash would be worse than the crash. Same shape as Gaming Profile's leftover-session recovery, which is the in-house pattern for exactly this. Writer and reader go through one shared CrashMarker record rather than an anonymous object: if they drifted, every crash would be recorded into a file that parses to nothing and nobody would ever be told. There is a test asserting they agree. The read deletes the marker, so one crash notifies once; a malformed or stale marker is also deleted, since a marker that is never cleared would prompt on every launch forever. Markers older than a week are dropped, and so are future-dated ones (clock change, or a file copied from another machine). The marker carries the exception type and message but no stack trace and no paths. Unlike the log, it is not scrubbed of the user name, and it exists to answer "did the last run crash?" — not to duplicate the log. Asserted, not just intended. The write runs inside a dying process, so it is wrapped in a broad catch that logs: a security or serialization fault there must not turn a recorded crash into a silent exit. That matches the two existing last-resort catches in this file (the CLI entry point and the single-instance pipe loop), both documented the same way. Verification: 27 checks against the BUILT assembly — the dialog text, the writer/reader round-trip including a null exception, the full launch cycle on disk (crash -> reported once -> marker gone), malformed/stale/future-dated markers, and DI resolution as a singleton. 27/27, plus 22 new xUnit tests. All four projects build with 0 warnings and 0 errors; leak scan across all 32 patterns: zero hits. Also deleted docs/screenshots/24-bandwidth-monitor.png (#1659). It showed the tab while it was still a placeholder — "Work in Progress" pill, a doubled "##337", v1.51.6 in the footer — for a feature the README calls finished. The README reference was already removed earlier and replaced with an honest note; this removes the orphaned 263 KB file, which nothing referenced. Recapturing it needs the secondary workstation, so that half stays open.
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.
Closes the crash-reporting half of #1651 and the leftover placeholder screenshot in #1659. Both claims verified against current source before any code was written.
The dialog said nothing useful
An unhandled dispatcher exception showed
Exception.Messagealone. For the commonest fault that reads "Object reference not set to an instance of an object." — nothing the target persona can act on, and no hint that a log exists or where it is.The app knew exactly where the evidence went and didn't say. So the report arrives as "it just closed", with nothing attached.
Now the message states what happened, that the app is still running (
OnUisetse.Handled = trueand continues — implying otherwise would push the user into force-closing a working app), and the exact folder. A wrapper exception's generic text is skipped in favour of its inner message, and a blank message falls back to something readable instead of an empty dialog.Built from string literals, the exception text, and one static property — deliberately nothing touching DI, the theme, or any service, since any of those may be what just faulted. The existing
MessageBoxstays for the same reason its comment already argues.A fatal crash was completely silent
OnDomainlogged the exception and let the process die with no UI at all: the window vanished, and nothing on the next start said the previous session ended badly.It now writes a marker, and Dashboard's init surfaces it once — a toast, not a dialog, because this is information, and a modal blocking every launch after one crash would be worse than the crash itself. Same shape as Gaming Profile's leftover-session recovery, which is the in-house pattern for exactly this.
Design points that matter:
CrashMarkerrecord, not an anonymous object. If they drifted, every crash would be written to a file that parses to nothing and nobody would ever be told. There's a test asserting they agree.Verification
27 checks against the built assembly, since the local xUnit suite isn't run on this workstation:
ExceptionObject(it's typedobject, so the cast can legitimately yield null)27/27, plus 22 new xUnit tests. All four projects: 0 warnings, 0 errors. Leak scan across all 32 patterns: zero hits.
The harness also printed the sentence a user actually sees, which is the thing worth eyeballing:
No exception type, no path — both useless to the persona — and it points at a tab she can actually reach.
Also: #1659
Deleted
docs/screenshots/24-bandwidth-monitor.png. It showed the tab while it was still a placeholder — "Work in Progress" pill, a doubled##337,v1.51.6in the footer — for a feature the README calls finished. I opened the file to confirm rather than trusting the issue.The README reference was already removed earlier this session and replaced with an honest note, so this just removes the orphaned 263 KB file that nothing referenced. Recapturing it needs the secondary workstation, so #1659 stays open for that half.
Not in scope
The issue also proposes a diagnostics-bundle button on the crash card. That's #1650's subject and a larger piece of work; this PR does the part that makes a crash reportable at all.