Skip to content

fix(updater): explain failures in the user's terms, and make the macOS close button work - #160

Merged
oesukam merged 4 commits into
mainfrom
fix/updater-errors-and-macos-close
Sep 5, 2026
Merged

fix(updater): explain failures in the user's terms, and make the macOS close button work#160
oesukam merged 4 commits into
mainfrom
fix/updater-errors-and-macos-close

Conversation

@oesukam

@oesukam oesukam commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Two unrelated fixes from the same report.

1. The update error said nothing useful

Update Failed
Could not fetch a valid release JSON from the remote

I checked the live endpoint first: it returns HTTP 200, valid JSON, v0.5.2, all 12 platforms signed. Nothing was wrong with the release — and indeed the update worked after a restart. It was a transient network failure.

Which is the point. The message is the raw Tauri string: accurate for whoever wired the updater up, useless for whoever is running the app. It names an internal artifact (release JSON) and gives no indication that simply retrying would work.

Now the recognisable failures map onto plain guidance:

Cause What the user is told
fetch / release JSON Check your connection — GitHub may be having trouble
signature / verify Don't retry — download the release manually
network / timeout The download timed out
permission / read-only Move out of a read-only location and retry

The signature case matters most: retrying a signature failure is the wrong move, and the old message gave no hint of that.

The raw text moves into a collapsed Technical details block — still there for a bug report, no longer the first thing to parse.

Five unit tests, including one using the exact string you saw.

2. The macOS close button did nothing

Closing with the red button left the app running with no window; only Quit actually exited.

Two halves of the same platform default:

  • macOS destroys the window on close rather than hiding it.
  • The app called .run() with no RunEvent handling, so there was no Reopen hook.

Net effect: the window vanished, the process lived on, and clicking the dock icon had nothing to restore.

CloseRequested now hides the window instead of destroying it, and Reopen shows it again — the macOS convention that close is not quit. Both are #[cfg(target_os = "macos")]; other platforms keep their behaviour, where closing the last window does exit.

Worth noting: my first attempt added only the Reopen handler. That compiles and looks right, but get_webview_window("main") returns None after the window is destroyed — it would have silently done nothing. The close interception is what makes the pair work.

It also collided with an existing safety net. Startup already spawns a timer that shows the window if it is still hidden after 3s, guarding against a frontend that never paints. Hiding on close looks identical to "never shown", so closing the window within the first three seconds would have popped it straight back up. A flag now distinguishes the two, and it is cfg-guarded so non-macOS builds (where closing still exits) stay warning-free.

Verification

Typecheck clean · 370 unit tests (5 new) · 70/70 e2e · lint 2 warnings / 0 errors · cargo fmt + clippy -D warnings clean.

The macOS behaviour needs a manual check — window lifecycle can't be exercised by CI or the browser-mode e2e suite. After merging, please confirm: red button hides the window, dock icon brings it back, ⌘Q still quits.

The grid could sit on "Running…" indefinitely — "0 rows · 12.0 s" and
climbing — with no way out. `running` is cleared only when a `done` batch
arrives or `api.stream` throws, and a wedged connection produces neither:
the call resolves, then no batch ever comes. There is a connect timeout
in the Postgres driver but no query/stream timeout anywhere.

Adds a 20s stall watchdog to the table and query views. If no batch has
arrived by then it clears `running`, shows "No response from the
database — the connection may have dropped", and best-effort cancels the
server-side handle so it isn't left running.

The e2e test drives a genuinely stalled stream (the mock resolves the
call and emits nothing) and fails when the watchdog is disabled, so it
covers the behaviour rather than just the happy path.
Opening a table on a MongoDB connection could fail with

    driver error: expected `db.<collection>.<method>(…)` or a `db.<helper>(…)` call

`TableDataTab` picks the dialect from `engine`, which is undefined while
the sessions and connections lists hydrate. `undefined !== "mongo"` fell
through to buildSql, so Postgres SQL went to the Mongo driver. The code
already carried a comment about this hazard — the subscription was fixed
so the engine resolves, but the undefined window was never guarded.

Now no query is built until the engine is known, and `run()` clears any
spinner instead of leaving one hanging. The effect re-runs once the
engine resolves, since `sql` is part of its key.

The mock backend gains a stall hook for the watchdog test. It lives in
`mockInvoke`, which `invoke` returns before reaching when `isTauri` is
true, so it cannot execute in the desktop app.
A failed check showed the raw Tauri message — "Could not fetch a valid
release JSON from the remote". That is accurate for whoever wired the
updater up and useless to whoever is running the app: it doesn't say what
went wrong or what to do, and in this case the answer was simply "try
again" (the failure was transient; the update worked after a restart).

Maps the errors we can recognise onto plain guidance:

  - fetch/release JSON  -> check your connection, GitHub may be down
  - signature/verify    -> don't retry, download manually
  - network/timeout     -> the download timed out
  - permission/denied   -> move out of a read-only location

The raw text moves into a collapsed "Technical details" block, so a bug
report still carries it without the user having to read it first.

The mapping lives in its own module with five tests, one of which uses
the exact string the updater produced.
Closing the window with the red button appeared to do nothing — the app
stayed running with no window, and Quit was the only way out.

Two halves of the same platform default. macOS destroys the window on
close rather than hiding it, and the app had no RunEvent handling, so:
the window went away, the process lived on, and clicking the dock icon
had nothing to restore.

Now CloseRequested hides the window instead of destroying it, and Reopen
shows it again — which is the macOS convention (close is not quit; the
dock icon brings the app back). Both are macOS-only; other platforms keep
their own behaviour, where closing the last window does exit.
@oesukam
oesukam force-pushed the fix/updater-errors-and-macos-close branch from d3e9ab1 to ea4a00b Compare September 5, 2026 18:59
@oesukam
oesukam merged commit 6675807 into main Sep 5, 2026
6 of 8 checks passed
@oesukam
oesukam deleted the fix/updater-errors-and-macos-close branch September 5, 2026 19:04
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