Skip to content

GUI: clean recovery UI for a server DB-fatal/"invalidated" state (not the raw DuckDB message) #81

Description

@jozef2svrcek

What happened

After a --fast TWIC import hit a fatal DuckDB error, the database entered DuckDB's invalidated state ("database has been invalidated because of a previous fatal error … must be restarted prior to being used again"). The raw error string then surfaced verbatim in the Home screen's "Automatic updates" panel, e.g.:

Error: FATAL Error: Failed: database has been invalidated because of a previous fatal error. … Original error: "Invalid Input Error: Failed to delete all rows from index. Only deleted 0 out of 1 rows. …"

A sudo systemctl restart lpdo-server fully recovered it (on-disk DB intact — the failed work rolled back on reopen).

Ask

Detect the server-fatal / DB-invalidated state and present a clean recovery card ("The server hit a database error and needs to restart") with a clear action — instead of dumping the raw DuckDB message into a random panel. The error also shouldn't leak into the "Automatic updates" (scheduler/status) section as a raw string; that panel should render a tidy error state.

Design notes

  • A "Restart server" button is non-trivial for a system daemon (privileged): sudo systemctl restart lpdo-server (Linux), launchctl kickstart -k system/com.specure.lpdo.server (macOS), sc stop/start LPDOServer (Windows) all need elevation the GUI doesn't have. Options, best first:
    1. Server-side auto-recovery (preferred): catch DuckDB's "invalidated / must be restarted" error in serve.rs/jobs.rs and reopen the DB connection in-process (new Connection) so it self-heals with no user action and no OS restart. The user never sees this.
    2. A /restart endpoint the GUI can call (daemon re-execs, or exits with the unit set to Restart=always).
    3. GUI shows the platform-specific restart command for the user to run (no in-GUI privilege). Lowest effort, worst UX.
  • Detect the state from /status (and other endpoints) returning the fatal/invalidated error, and switch the whole app into the recovery state rather than rendering per-panel raw strings.

Related (separate, deeper — root cause)

The --fast import is appender-based and not crash-safe, so a mid-write failure (here: Failed to delete all rows from index on a player row) can drop the DB into the invalidated state. Worth its own issue: (a) why the fast-import index op failed (DuckDB ART index / dedup interaction, possibly version-specific), and (b) making the import fail more gracefully (transactional, or checkpoint/guard) so it can't invalidate the live DB. Combined with the auto-recovery above, the user would never hit a dead server.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions