Skip to content

fix(desktop): first-run setup no longer requires input - #43

Merged
huyplb merged 2 commits into
mainfrom
fix/tauri-error-message-swallowed
Jul 7, 2026
Merged

fix(desktop): first-run setup no longer requires input#43
huyplb merged 2 commits into
mainfrom
fix/tauri-error-message-swallowed

Conversation

@huyplb

@huyplb huyplb commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Summary

A user hit a bare "Setup failed." on the desktop app's first-run wizard with no further detail, then — once diagnosable — a genuine setup failure that turned out to be two separate real bugs, both found by actually running the fixed app end-to-end.

1. The error-swallowing bug. Tauri commands return Result<T, String> on the Rust side, so a failed command rejects invoke() with a plain string, not a JS Error. Every call site's catch (err) { err instanceof Error ? err.message : fallback }SetupScreen, both error paths in DatabaseSettings — always took the generic fallback and hid whatever Rust actually returned. Fixed once at the IPC boundary in invokeTauri() (apiBase.ts).

2. First-run wizard needs no input. Previously required a real email (bound to the encryption key) and let you edit the database path before finishing. Now it completes automatically: email defaults to local@foxschema.app (same placeholder the web edition's single-user mode already uses), database stays on its default SQLite location, no form — just a brief "Securing this install…" moment, or a real error + Retry button if something fails.

3. New "Change email" in Settings → Security, since it's no longer set at setup time. New update_email Tauri command re-stores the same key material under the new email's keychain account — no re-encryption, no sidecar respawn.

4. Two real bugs found by actually running the above, both hidden by bug #1 until now:

  • SetupScreen's auto-attempt effect only guarded against a stray setState after unmount — it didn't stop completeSetup() from firing twice under React 18 StrictMode's dev-mode double-invoke, racing two sidecar subprocesses against the same SQLite file ("database is locked"). Fixed with a ref that survives the mount/unmount/remount cycle (the standard StrictMode-safe pattern for effects with real side effects).
  • enforce_install_binding's JSON parse used stdout.lines().last(), but the captured stdout can carry more than one trailing newline under tauri_plugin_shell's capture (not reproducible via a direct shell invocation of the identical command) — making .lines().last() return an empty line instead of the JSON, always failing regardless of whether the check actually succeeded. Fixed by trimming the whole capture before splitting.
  • Also added error logging for both of enforce_install_binding's failure paths — this check's output went nowhere on failure before, which is exactly why bug was invisible.

Test plan

  • cargo check (Rust) and cd apps/web && npx tsc --noEmit — both clean.
  • npx vitest run — 221 passed, 2 skipped.
  • Verified invokeTauri normalizes a plain-string rejection into a real Error (mocked window.__TAURI__.core.invoke).
  • Verified the simplified SetupScreen end-to-end in-browser (error/Retry states) and, separately, ran the actual desktop app (npm run dev) through several clean first-runs — reproduced both bug feat: E2E test suite, SQL generator fixes, security automation, versioning #4 issues live via added diagnostic logging, fixed each, and confirmed a completely clean first run: setup_complete: true, sidecar API listening, no errors.
  • Verified the new EmailSettings Change → edit → Save flow end-to-end in-browser (mocked Tauri + /api/app-info).

huyplb and others added 2 commits July 6, 2026 22:02
…allback

Tauri commands return Result<T, String> on the Rust side, so a failed
command rejects invoke() with a plain string, not an Error instance.
Every call site's catch (err) { err instanceof Error ? err.message :
fallback } pattern — SetupScreen's first-run wizard, and both error
paths in DatabaseSettings — was therefore always taking the generic
fallback ("Setup failed.", "Connection failed", "Failed to switch
database") and silently discarding the actual reason Rust returned
(e.g. "A valid email is required.", a keychain failure, a bad
connection string).

Fixed once at the IPC boundary in invokeTauri() rather than patching
each catch site: normalize whatever invoke() rejects with into a real
Error, so err.message downstream is always the genuine message.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The wizard required an email and let you edit the database path before
finishing setup — every validation failure (bad email, keychain error,
a copied-DB refusal) surfaced as an opaque error with no field to even
retry from, since the previous commit's swallowed-error bug hid what
actually went wrong.

Setup now completes automatically on first launch: email defaults to
local@foxschema.app (the same placeholder the web edition's
single-user mode already uses) and the database stays on its default
SQLite location. No form, no required input — just a brief "Securing
this install…" moment, or a real error message with a Retry button if
something genuinely fails (e.g. a copied database refused by
enforce_install_binding).

Since email is no longer entered at setup, added a way to set a real
one afterward: Settings → Security now has an editable "Change" action
backed by a new update_email Tauri command. It re-stores the SAME key
material under the new email's keychain account (the DEK itself never
changes, so already-encrypted data keeps decrypting) and best-effort
removes the old account — no sidecar respawn needed, since nothing
about the metadata DB changes.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@huyplb huyplb changed the title fix(web): surface the real Tauri command error instead of a generic fallback fix(desktop): first-run setup no longer requires input Jul 7, 2026
@huyplb
huyplb merged commit 4db32c0 into main Jul 7, 2026
4 of 6 checks passed
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