Skip to content

fix(web_server): auto-migrate lagging profile DBs in /api/profiles/sessions - #42487

Closed
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:fix/desktop-profile-session-schema-migration
Closed

fix(web_server): auto-migrate lagging profile DBs in /api/profiles/sessions#42487
liuhao1024 wants to merge 1 commit into
NousResearch:mainfrom
liuhao1024:fix/desktop-profile-session-schema-migration

Conversation

@liuhao1024

Copy link
Copy Markdown
Contributor

Problem

On a multi-profile install, the Desktop sidebar shows no sessions for a profile when that profile's state.db schema lags the default DB's schema. This happens after an update bumps the SessionDB schema — the default DB is migrated on gateway startup, but per-profile DBs are only ever opened read-only by the /api/profiles/sessions aggregation endpoint.

When list_sessions_rich queries s.archived against a DB missing that column, it raises sqlite3.OperationalError. The endpoint silently swallows the error into errors[], contributing zero sessions for that profile — the Desktop renders an empty sidebar with no indication of what went wrong.

Closes #42467

Solution

Catch sqlite3.OperationalError from the read-only query path. When a schema mismatch is detected:

  1. Close the read-only connection
  2. Open the DB read-write to trigger _init_schema()_reconcile_columns() (adds missing columns via ALTER TABLE ADD COLUMN)
  3. Close the read-write connection
  4. Re-open read-only and retry the query

This is a one-time cost per lagging profile. After migration, the read-only path works normally. The write lock is brief (column reconciliation only) and only happens when the schema is actually outdated.

Changes

  • hermes_cli/web_server.py — Wrap list_sessions_rich/session_count calls in an inner try/except for sqlite3.OperationalError; on schema mismatch, open read-write to trigger migration, then retry
  • tests/hermes_cli/test_profile_schema_migration.py — 2 new tests:
    • test_profiles_sessions_auto_migrates_lagging_schema — verifies a lagging profile DB returns sessions after auto-migration
    • test_profiles_sessions_migration_is_idempotent — verifies second call works without re-migration

Testing

$ python -m pytest tests/hermes_cli/test_profile_schema_migration.py -xvs
2 passed

$ python -m pytest tests/hermes_cli/test_web_server.py -k "profile" -x
27 passed

@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard labels Jun 9, 2026
@alt-glitch alt-glitch added comp/dashboard Web dashboard / control panel UI (dashboard/, landing) sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades labels Jun 26, 2026

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for identifying a real regression in the read-only cross-profile path. Current main still opens each profile DB read-only at hermes_cli/web_server.py:4137, and SessionDB intentionally skips reconciliation on that path at hermes_state.py:957-974; the s.archived filters at hermes_state.py:3330-3333 can therefore hide a lagging profile behind errors[].

Problems

  • The proposed broad sqlite3.OperationalError catch (hermes_cli/web_server.py:1937 in this PR's diff) also catches lock and other operational failures, then opens the live profile read-write. That conflicts with the read-only path's stated no-write-lock purpose. A writable SessionDB runs full _init_schema() (hermes_state.py:992-994), not only column reconciliation.
  • The migration exception is swallowed, so the retry loses the original failure context.

Suggested changes

  • Restrict recovery to a verified missing-column schema error; retain normal handling for lock/contention and other operational errors.
  • Add a test proving a non-schema OperationalError never opens the profile writable.
  • Rebase the logic manually onto the current endpoint while preserving compact_rows=not full at hermes_cli/web_server.py:4151-4152.

Automated hermes-sweeper review.

Comment thread hermes_cli/web_server.py Outdated
min_message_count=min_message_count,
include_archived=include_archived,
archived_only=archived_only,
exclude_children=True,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This catches every SQLite operational failure, including database is locked, then opens another profile's DB read-write. Please limit the migration retry to a verified missing-column/schema error and preserve normal degradation for lock/contention failures; the read-only aggregation exists specifically to avoid write-locking live profile DBs.

…olumn errors only

Address feedback from NousResearch#42487 review:

- Restrict OperationalError catch to verified missing-column schema errors
  (check error message for "no such column")
- Retain normal handling for lock/contention and other operational errors
- Preserve read-only path's no-write-lock purpose by not opening profile
  writable on non-schema errors
- Add regression tests for auto-migration and idempotent retries

Fixes: NousResearch#42487
@liuhao1024
liuhao1024 force-pushed the fix/desktop-profile-session-schema-migration branch from 4ef257d to 0a9b361 Compare July 14, 2026 07:56
@teknium1 teknium1 added sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform area/sessions Session lifecycle, resume, persistence, history area/profiles Multi-profile isolation, HERMES_HOME scoping labels Jul 14, 2026
@claudeschneider

Copy link
Copy Markdown

This PR would prevent a recurring multi-profile Desktop support issue.

Current tree still opens profile DBs read_only=True in both:

  • GET /api/profiles/sessions
  • GET /api/profiles/sessions/sidebar (batched path Desktop uses now)

Without this (or equivalent migrate-on-first-sidebar-load / post-update migrate-all-profiles), Desktop users still see empty sidebars after schema-bumping updates while CLI proves sessions intact.

Fresh report with multi-profile repro + update-path asks:
#70944

If this PR is still viable, please also cover the sidebar batched endpoint, not only the older list endpoint.

@Tilly-YL

Tilly-YL commented Aug 6, 2026

Copy link
Copy Markdown

Thank you for tracing the read-only migration gap. I prepared #80030 against current main, using the shared profile open helper and covering both /api/profiles/sessions and /api/profiles/sessions/sidebar. I added your name as a co-author because this follows the approach you introduced here.

@OutThisLife

Copy link
Copy Markdown
Collaborator

Closing as superseded by #80797 (merged, bdee48928).

The stale-schema heal now lives in one place — _open_session_db_at_path() probes against SCHEMA_SQL and does a one-time writable reopen before serving reads — rather than as an inline retry inside get_profiles_sessions. That covers this route plus every other read-only profile open, and any future column addition automatically.

Your premise was correct, and this is the bug that bit users after the 0.20.0 update (#79531 / #80037); the centralized version is just the wider fix. Thanks for filing it as early as you did.

@OutThisLife OutThisLife closed this Aug 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/profiles Multi-profile isolation, HERMES_HOME scoping area/sessions Session lifecycle, resume, persistence, history comp/cli CLI entry point, hermes_cli/, setup wizard comp/dashboard Web dashboard / control panel UI (dashboard/, landing) P3 Low — cosmetic, nice to have sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Desktop per-profile session list silently empty when a profile's state.db schema lags the default DB

6 participants