hermes - ✅(Solved) Fix [Bug]: Refreshing or deep-linking to a dashboard plugin tab redirects to /sessions [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#18660Fetched 2026-05-03 04:55:05
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Author
Participants
Timeline (top)
labeled ×3cross-referenced ×1

Error Message

URL is rewritten to /sessions immediately on first render. There is no error in the browser console — the redirect is intentional code, just firing before manifests arrive.

Relevant Logs / Traceback

Root Cause

Root Cause Analysis (optional)

Fix Action

Fixed

PR fix notes

PR #18661: fix(dashboard): defer unknown-route redirect while dashboard plugins load

Description (problem / solution / changelog)

Summary

Follow-up to #16906. That PR fixed the dashboard routing race for /chat by parking a placeholder ChatRouteSink route so the * catch-all wouldn't fire on it. The same race still hits arbitrary plugin tab paths (/kanban, /achievements, ...): on refresh or deep-link the <Route path="*"> element fires before plugin manifests are fetched and rewrites the URL to /sessions. Plugin paths can't be hardcoded as placeholders the way /chat can — they come from /api/dashboard/plugins at runtime — so the fallback itself needs to defer.

Reproduction (on my gateway, with the bundled kanban plugin)

actionbeforeafter
sidebar click → /kanbanrenders kanbanrenders kanban
refresh on /kanbanURL replaced with /sessions, kanban hiddenbrief spinner, then /kanban renders
open /kanban in a new tabURL replaced with /sessionsbrief spinner, then /kanban renders
open genuinely unknown /foobarredirect to /sessionsspinner ≤2 s, then /sessions

Approach

  • Extract the * route element into UnknownRouteFallback({ pluginsLoading }).
  • While pluginsLoading is true, render a small Loading dashboard plugins… spinner instead of an immediate <Navigate to="/sessions" replace />.
  • When pluginsLoading clears, the routes memo has already rebuilt with the plugin manifests, so the matching plugin path claims the URL and the fallback no longer renders. For paths that are actually unknown, the redirect fires as before.
  • Reuses the existing pluginsLoading state from usePlugins. The manifest-fetch failure (catch) and no-plugins-installed branches already flip it to false synchronously, so broken setups still fall through to the redirect immediately. The usePlugins 2 s safety timeout caps the worst case for genuinely unknown paths.

Tests

No automated test added — web/ does not currently have a component-test harness, and the change is one conditional element. Verified manually on my gateway:

  • tsc --noEmit and npm run build clean.
  • Refresh and new-tab deep-link on /kanban both land on /kanban after a short spinner.
  • /foobar-not-a-route still redirects to /sessions.

Closes

Closes #18660

Changed files

  • web/src/App.tsx (modified, +22/-1)

Code Example

version:          0.12.0 (2026.4.30) [f98b5d00]
os:               Linux 5.15.0-174-generic x86_64
python:           3.11.15
openai_sdk:       2.30.0
profile:          default
hermes_home:      ~/.hermes
model:            gpt-5.5
provider:         openai-codex
terminal:         local

features:
  toolsets:           hermes-cli
  mcp_servers:        0
  memory_provider:    built-in
  gateway:            running (systemd (user), pid 952636)
  platforms:          discord
  cron_jobs:          0 active / 0 total
  skills:             181

---

<Route path="*" element={<Navigate to="/sessions" replace />} />
RAW_BUFFERClick to expand / collapse

Bug Description

Refreshing the dashboard while on a plugin tab — or deep-linking to one — rewrites the URL to /sessions and the plugin tab does not render. Sidebar clicks still work, but any deep-link or page refresh effectively makes the plugin tab "disappear" until clicked again.

The race is in web/src/App.tsx: the <Route path="*"> element is <Navigate to="/sessions" replace />, and web/src/plugins/usePlugins.ts fetches plugin manifests asynchronously from GET /api/dashboard/plugins. Until manifests resolve, manifests = [] and buildRoutes() only emits built-in routes, so any plugin path matches * and is rewritten before the manifest list arrives. By the time manifests resolve, the URL has already been replaced.

PR #16906 fixed the same race for /chat by parking a placeholder ChatRouteSink route; that trick only works for built-in paths known at compile time, so plugin paths still hit the original race.

Steps to Reproduce

  1. Have at least one dashboard plugin that registers a tab installed (e.g. the bundled kanban plugin).
  2. Open the dashboard, click the Kanban tab — URL becomes /kanban, page renders.
  3. Refresh the page (or open /kanban directly in a new tab).
  4. Observe: URL is replaced with /sessions, kanban tab does not render.

Reproduces against main at f98b5d00a with --insecure dashboard on localhost:9119.

Expected Behavior

Stay on /kanban (after a brief plugin-load window) and render the kanban page.

Actual Behavior

URL is rewritten to /sessions immediately on first render. There is no error in the browser console — the redirect is intentional code, just firing before manifests arrive.

Affected Component

Other (Web Dashboard / dashboard plugin system)

Messaging Platform (if gateway-related)

N/A (CLI only)

Debug Report

version:          0.12.0 (2026.4.30) [f98b5d00]
os:               Linux 5.15.0-174-generic x86_64
python:           3.11.15
openai_sdk:       2.30.0
profile:          default
hermes_home:      ~/.hermes
model:            gpt-5.5
provider:         openai-codex
terminal:         local

features:
  toolsets:           hermes-cli
  mcp_servers:        0
  memory_provider:    built-in
  gateway:            running (systemd (user), pid 952636)
  platforms:          discord
  cron_jobs:          0 active / 0 total
  skills:             181

Operating System

Ubuntu 24.04

Python Version

3.11.15

Hermes Version

f98b5d00a (main, dashboard built locally)

Relevant Logs / Traceback

No server-side log — purely a client-side React Router race in web/src/App.tsx. Reproducible by watching the address bar replace /kanban with /sessions immediately after navigation.

Root Cause Analysis (optional)

web/src/App.tsx:

<Route path="*" element={<Navigate to="/sessions" replace />} />

web/src/plugins/usePlugins.ts initializes loading=true, fetches manifests asynchronously, and only flips loading=false once manifests are fetched and plugin scripts have registered (or after a 2 s safety timeout). Until then, routes from buildRoutes() contains only the built-in routes, so plugin paths fall through to * and the redirect rewrites the URL.

Proposed Fix (optional)

Defer the * redirect while pluginsLoading is true: render a small spinner during the plugin-load window and only <Navigate to="/sessions"> once pluginsLoading clears. By that point the routes memo has rebuilt with the plugin routes and React Router will render the matching plugin page instead of the fallback. PR follows.

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

extent analysis

TL;DR

Defer the * redirect in web/src/App.tsx while pluginsLoading is true to prevent the URL from being rewritten to /sessions before plugin manifests are fetched.

Guidance

  • Identify the pluginsLoading state in web/src/plugins/usePlugins.ts and pass it as a prop to the App component in web/src/App.tsx.
  • Conditionally render a loading indicator or a placeholder instead of the <Navigate to="/sessions" replace /> component when pluginsLoading is true.
  • Once pluginsLoading becomes false, allow the normal routing to take place, which should now include the plugin routes.
  • Verify that the fix works by refreshing the page or deep-linking to a plugin tab and checking that the URL remains correct and the plugin tab renders as expected.

Example

// web/src/App.tsx
import { pluginsLoading } from './plugins/usePlugins';

function App() {
  if (pluginsLoading) {
    return <div>Loading plugins...</div>; // or a spinner component
  }

  return (
    // normal routing configuration
    <Route path="*" element={<Navigate to="/sessions" replace />} />
  );
}

Notes

This fix assumes that the pluginsLoading state is correctly updated in web/src/plugins/usePlugins.ts and that the buildRoutes() function is called again after the manifests are fetched.

Recommendation

Apply the proposed fix by deferring the * redirect while pluginsLoading is true, as this should resolve the routing issue without introducing significant changes to the existing codebase.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix [Bug]: Refreshing or deep-linking to a dashboard plugin tab redirects to /sessions [1 pull requests, 1 participants]