Skip to content

fix: wait for the server to start before requesting settings - #1511

Merged
harshithmohan merged 2 commits into
masterfrom
fix/settings-wait-for-server
Oct 17, 2026
Merged

harshithmohan merged 2 commits into
masterfrom
fix/settings-wait-for-server

Conversation

@hidden4003

@hidden4003 hidden4003 commented Oct 17, 2026 •

Copy link
Copy Markdown
Member

Why

When the stored apikey is no longer valid (for example, after the server's tokens were deleted), the WebUI stays on the boot spinner forever instead of going to the login page.

#1452 excluded /api/v3/Settings from the 401 logout in src/core/axios.ts. Settings is [InitFriendly] on the server, so it stays reachable while the server is starting, and until startup finishes the server rejects every apikey with 401. Without the exclusion, opening the WebUI or restarting the server would log the user out, even with a valid key.

The exclusion has a side effect. MainPage waits for a real Settings response before it renders anything else. With a bad key, Settings is the only request that fails and nothing logs out, so the spinner never goes away.

What

Nothing under the authenticated routes is rendered, and Settings is not requested, until Init/Status reports Started. The Settings exclusion is removed.

  • AuthenticatedRoute now waits for the server state instead of rendering its children optimistically while the state is not loaded. Starting and Failed already show the StatusPage, so no page, including one opened by direct url, can request data before the server has started. While the state is loading, an authenticated user sees the same spinner as MainPage.
  • Router has its own Settings observer (for the theme) above the route guard, so it only enables it once the state is Started.
  • Once the server is Started, a 401 from Settings means the key was rejected, so the existing 401 logout handles it like any other endpoint.
  • src/core/axios.ts loses one line. No requests are made from the interceptor.

Supersedes #1491

#1491 fixed the same bug a different way, and I think that approach was wrong. It kept the interceptor aware of startup by firing a GET User/Current probe whenever another endpoint returned 401, and logged out only if the probe also returned 401.

  • Interceptor makes requests. That is a new pattern. apikeyCheckPending only keeps one probe in flight, so while the server starts (every authenticated call returns 401) or a plugin endpoint keeps returning its own 401, a new probe is sent each time the previous one settles.
  • The spinner can come back. If Settings returns 401 while the server is starting and the probe then returns 200 because the server just finished starting, the session is kept but the failed Settings query is never retried. The user stays on the spinner.
  • It left the cause untouched. Settings was still requested before the server had started.

Gating the request avoids all three, with a few lines instead of a new mechanism, new tests and an AGENTS.md rule change. #1491 can be closed in favour of this PR.

Where to look

  • src/core/router/AuthenticatedRoute.tsx: waits for the server state before rendering.
  • src/core/router/index.tsx: the Settings query gate in Router.
  • src/core/axios.ts: the removed Settings exclusion.

Testing

  • pnpm tscheck and pnpm test pass, and the pre-commit hooks pass. pnpm build was not run.
  • Manual test against a running server:
    • Bad key: with a stored session that has an invalid apikey, opening /webui/ goes to the login page and the stored session is cleared (localStorage.apiSession is null). On master it stays on the spinner.
    • Valid key, startup: reloading the WebUI while the server is starting shows the spinner, the StatusPage, then the dashboard, with the session kept. Opening a deep link such as /webui/settings/general during startup behaves the same.

Settings rejects every apikey with 401 until the server has started, so it was excluded from the 401 logout. That left a rejected apikey stuck on the boot spinner. Only request settings once Init/Status reports Started, which makes the exclusion unnecessary.
AuthenticatedRoute rendered its children while Init/Status had not loaded yet, so any page opened by direct url could request data before the server had started. Wait for the state there instead of gating Settings in MainPage.
@harshithmohan
harshithmohan merged commit 3da5d81 into master Oct 17, 2026
3 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.

2 participants