Skip to content

fix: tell the user where crash details are, and stop a fatal crash from being silent - #1708

Merged
laurentiu021 merged 1 commit into
mainfrom
fix/readme-screenshot-and-uninstaller-admin
Aug 6, 2026
Merged

fix: tell the user where crash details are, and stop a fatal crash from being silent#1708
laurentiu021 merged 1 commit into
mainfrom
fix/readme-screenshot-and-uninstaller-admin

Conversation

@laurentiu021

Copy link
Copy Markdown
Owner

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.Message alone. 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 (OnUi sets e.Handled = true and 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 MessageBox stays for the same reason its comment already argues.

A fatal crash was completely silent

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 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:

  • Writer and reader share one CrashMarker record, 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.
  • The read deletes the marker, so one crash notifies exactly once. A malformed or stale marker is deleted too — one that is never cleared would prompt on every launch forever.
  • Stale and future-dated markers are dropped (older than a week; clock change or a file copied from another machine).
  • No stack trace, no paths. Unlike the log, this file is not scrubbed of the user name. 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's wrapped in a broad catch that logs — a security or serialization fault there must not turn a recorded crash into a silent one. That matches the two existing last-resort catches in this file (CLI entry point, single-instance pipe loop), documented the same way.

Verification

27 checks against the built assembly, since the local xUnit suite isn't run on this workstation:

  • dialog text: names the folder, keeps the error, says still running, prefers the inner exception, survives blank/null
  • writer→reader round-trip, including a null ExceptionObject (it's typed object, so the cast can legitimately yield null)
  • the full launch cycle on disk: crash → reported once → marker consumed
  • malformed / stale / future-dated markers all rejected and cleaned up
  • DI resolves it as a singleton

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:

SysManager closed unexpectedly on 5 August, 14:57. Details were saved to its log, which you can open from the System Logs tab if you want to report it.

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.6 in 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.

…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.
@laurentiu021
laurentiu021 merged commit debe86a into main Aug 6, 2026
4 checks passed
@laurentiu021
laurentiu021 deleted the fix/readme-screenshot-and-uninstaller-admin branch August 6, 2026 06:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant