NousResearch/hermes-agent · error
${res.status}: ${text}
Error message
${res.status}: ${text} What it means
This is the dashboard's generic REST failure: authedFetch throws `${status}: ${body-text}` whenever a /api/* request returns a non-OK status. The response body text (or statusText when the body is unreadable) is inlined so the caller sees the server's error detail. A preceding successful 2xx clears the stale-token reload guard, meaning repeated 401s from here indicate a genuinely invalid session.
Source
Thrown at web/src/lib/api.ts:180
// no-store`` so a reload picks up the freshly-injected token. Trigger
// that reload once on the first stale-token 401 — gated mode is
// handled above, so reaching here in gated mode means a real
// middleware failure that should not reload-loop.
if (!window.__HERMES_AUTH_REQUIRED__ && !options?.allowUnauthorized) {
if (attemptDashboardTokenReloadOnce()) {
return new Promise<T>(() => {});
}
}
}
if (res.ok) {
// Clear the stale-token reload guard: a successful 2xx proves the
// current ``window.__HERMES_SESSION_TOKEN__`` is valid, so the next
// 401 — if any — should be allowed to trigger its own reload cycle.
clearDashboardTokenReloadAttempt();
}
if (!res.ok) {
const text = await res.text().catch(() => res.statusText);
throw new Error(`${res.status}: ${text}`);
}
return res.json();
}
/** Encode a plugin registry key for URL paths (preserves `/` segment separators). */
function pluginPath(name: string): string {
return name.split("/").map(encodeURIComponent).join("/");
}
/**
* Fetch a single-use ticket for a WebSocket upgrade in gated mode.
*
* The dashboard's gated-mode WS auth (``hermes_cli.web_server._ws_auth_ok``)
* rejects the legacy ``?token=<_SESSION_TOKEN>`` path and only accepts
* ``?ticket=<minted>`` consumed against the in-memory ticket store. Browsers
* can't set ``Authorization`` on a WS upgrade, so this round-trip via the
* authenticated REST endpoint is the bridge from cookie auth to WS auth.
*View on GitHub (pinned to c896c09c42)
Solutions
- Read the numeric status from the start of the message: 401 → reload the page or re-authenticate so a fresh session token/cookie is issued; other statuses → address the server-side detail in the message body.
- Verify the gateway process is still running and the dashboard was served by it (not a stale cached SPA pointing at an old port).
- If the status is 404, confirm the API route exists in the running gateway version — rebuild/redeploy matching web assets.
Defensive patterns
Strategy: retry
Validate before calling
if (!window.__HERMES_SESSION_TOKEN__ && window.__HERMES_AUTH_REQUIRED__) {
location.reload() // re-bootstrap credentials before firing API calls
} Try / catch
try {
return await authedFetch<T>(path, init)
} catch (err) {
if (String(err).startsWith('401')) {
clearDashboardTokenReloadAttempt() // allow one reload cycle
location.reload()
return new Promise<T>(() => {}) // halt callers while reloading
}
throw err
} Prevention
- Treat the leading status code as the dispatch key (401 re-auth, 404 not-found, 5xx retry/log).
- Never build UI that swallows this error silently — surface the server text.
- Keep gateway and web bundle versions in lockstep to avoid route-shape 404s.
When it happens
Trigger: Any dashboard REST call returning 4xx/5xx: 401 when the session token/cookie is expired in gated mode, 404 when a plugin/session resource no longer exists, 400 on malformed payloads, 500 from an unhandled gateway exception. `res.ok` is false and the body is read via res.text().
Common situations: Dashboard left open past session expiry, gateway restarted (invalidating in-memory tokens), multiple dashboard tabs after logout/re-login, or backend routes that changed between frontend and gateway versions (stale built web assets).
Related errors
- /api/auth/ws-ticket: HTTP ${res.status}
- text || HTTP ${res.status}
- Gateway did not return a WS ticket.
- Remote gateway session token is required.
- Remote Hermes gateway is selected, but no session token is s
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/69c08b7c0626fab5.
Report an issue: GitHub.