fix(sessions): heal stale profile schemas on read - #80030
Closed
Tilly-YL wants to merge 1 commit into
Closed
Conversation
Build the read-only schema probe from SCHEMA_SQL so newly added columns trigger the existing writable reconciliation path. Route both cross-profile session endpoints through the same helper, including the batched Desktop sidebar endpoint. Co-authored-by: liuhao1024 <sunsky.lau@gmail.com>
This was referenced Aug 6, 2026
Collaborator
|
Appreciate the contribution! Closed by #80797 |
5 tasks
teknium1
added a commit
that referenced
this pull request
Aug 15, 2026
…up and stop swallowing locked ALTERs After `hermes update`, an existing state.db on an old schema made every GET /api/sessions poll fail with sqlite3.OperationalError "no such column: s.last_read_at" (or s.last_activity_at) until something unrelated forced a writable open — the desktop sidebar showed "No sessions yet" while every row sat intact on disk (#79531, #80037). Two remaining root causes (the stale hand-written read probe was already replaced by the SCHEMA_SQL-derived probe on main, prototyped in draft PR #80030 by @Tilly-YL): 1. Migrations ran lazily: _init_schema/_reconcile_columns only ran on a writable open, typically the user's first NEW session. The dashboard backend now schedules one writable open of its own state.db from the lifespan (daemon thread, never blocks the ready-probe socket, never raises), so the store is brought current before the first session- list poll on every `hermes serve` / `hermes dashboard` / Desktop headless entrypoint. 2. _reconcile_columns caught sqlite3.OperationalError around every ALTER TABLE ADD COLUMN and logged at DEBUG. Lock contention from orphaned sibling backends made the ALTER fail silently — startup "succeeded" with a half-reconciled schema, and the open-time lock patience (#74478) never saw the error because it was swallowed inside first. Now: "duplicate column" races stay at DEBUG, locked/busy re-raises so _connect_and_init_with_lock_patience retries the whole idempotent init with jittered backoff, and any other failure (e.g. un-ADDable NOT NULL) logs at WARNING. Regression tests: a store missing sessions.last_read_at is healed by the eager startup reconcile and serves list_sessions_rich; a locked ALTER propagates and is retried to success by the open lock patience; duplicate-column races stay quiet; other ALTER failures warn. Fixes #79531 Fixes #80037 Reported-by: @yenhunghuang (#79531) and @FLOW3R0111 (#80037) Root-cause analysis: @wangyi0177-eng (stale read probe) and @www654cc-pixel (_reconcile_columns DEBUG-swallow under lock contention); draft PR #80030 by @Tilly-YL prototyped the probe fix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Hermes Desktop reads every profile's
state.dbin read-only mode when it builds the session list. Read-only opens skip schema reconciliation. The existing probe checked four named columns, so an older profile withoutsessions.last_read_atpassed the probe and failed later withno such column: s.last_read_at. The cross-profile list and batched sidebar routes also openedSessionDBdirectly. They caught that failure and returned an empty list, which made intact sessions appear missing after an update or restart.This change derives cached
LIMIT 0probes from every table and column declared inSCHEMA_SQL. A current database remains read-only. A stale profile follows the existing one-time writable reconciliation path and is reopened read-only. Both cross-profile session routes now use that shared helper.Related Issue
Fixes #79531
This follows the direction introduced in #42487. The commit retains liuhao1024's credit and extends the fix to the current shared helper and the Desktop sidebar route. The reproduction and schema analysis from wangyi0177-eng in #79531 helped isolate the
last_read_atcase.Type of Change
Changes Made
hermes_cli/web_server.pynow builds the read-only probes fromSCHEMA_SQLand uses the existing writable reconciliation path when any declared table or column is missing.hermes_cli/web_routers/profiles.pynow routes both/api/profiles/sessionsand/api/profiles/sessions/sidebarthrough_open_session_db_for_profile.tests/hermes_cli/test_web_server.pycovers missinglast_read_atandprofile_namecolumns, plus both cross-profile session endpoints.How to Test
SessionDB, add one session and one message, then removesessions.last_read_atto model a database created before that column was added./api/sessions,/api/profiles/sessions, and/api/profiles/sessions/sidebar. Each route should return the existing session, andPRAGMA table_info(sessions)should show thatlast_read_atwas restored.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
Screenshots / Logs
I also started
scripts/run_tests.sh. At 28.8%, the runner had reported 7,630 passing checks and 12 failures, so I stopped the run. The failures were outside the modified files and came from missing optional SDKs, the live-system signal guard, credential-routing expectations, and a network isolation test. The focused web server tests above all pass on the rebased commit.