NousResearch/hermes-agent · error
This Desktop build cannot refresh OAuth WebSocket tickets. U
Error message
This Desktop build cannot refresh OAuth WebSocket tickets. Update Hermes Desktop and try again.
What it means
Thrown by resolveGatewayWsUrl when the connection is in OAuth mode but the injected getGatewayWsUrl dependency (the ticket-minting bridge into the Electron host) is missing. OAuth-mode WebSocket URLs are short-lived tickets minted by the desktop main process; only it can refresh them, so a build or embedding without that bridge cannot reconnect.
Source
Thrown at apps/shared/src/websocket-url.ts:45
super(message, options)
this.name = 'GatewayReauthRequiredError'
}
}
export function isGatewayReauthRequired(error: unknown): error is GatewayReauthRequiredError {
return (
error instanceof GatewayReauthRequiredError ||
(typeof error === 'object' && error !== null && (error as { needsOauthLogin?: unknown }).needsOauthLogin === true)
)
}
export async function resolveGatewayWsUrl(deps: ResolveGatewayWsUrlDeps, conn: GatewayWsConnection): Promise<string> {
const mint = deps.getGatewayWsUrl
const profile = conn.profile ?? null
if (conn.authMode === 'oauth') {
if (!mint) {
throw new Error('This Desktop build cannot refresh OAuth WebSocket tickets. Update Hermes Desktop and try again.')
}
try {
const result = await mint(profile)
if (typeof result === 'string') {
return result
}
if (result.ok) {
return result.wsUrl
}
if (result.needsOauthLogin) {
throw new GatewayReauthRequiredError(
'Your remote gateway session has expired. Open Settings -> Gateway and click "Sign in" again.',
{ cause: new Error(result.error) }
)View on GitHub (pinned to c896c09c42)
Solutions
- Update Hermes Desktop so the main/preload process provides the gateway WS ticket API.
- If wiring the client manually, pass a getGatewayWsUrl dep that calls window.hermesDesktop's ticket endpoint whenever authMode is 'oauth'.
- In tests, inject a fake mint function instead of leaving it undefined.
Example fix
// before
resolveGatewayWsUrl({} as deps, conn) // oauth conn, no mint
// after
resolveGatewayWsUrl({ getGatewayWsUrl: (profile) => hermesDesktop.gateway.wsUrl(profile) }, conn) Defensive patterns
Strategy: validation
Validate before calling
function canMintOAuthTickets(deps: ResolveGatewayWsUrlDeps): boolean {
return typeof deps.getGatewayWsUrl === 'function'
}
// before constructing: if conn.authMode === 'oauth', require the mint dep Type guard
function hasMintDep(deps: ResolveGatewayWsUrlDeps): deps is ResolveGatewayWsUrlDeps & {
getGatewayWsUrl: (profile: null | string) => Promise<string | { ok: true; wsUrl: string } | { ok: false; needsOauthLogin?: boolean; error?: string }>
} {
return typeof deps.getGatewayWsUrl === 'function'
} Prevention
- Assert the mint dependency is present whenever constructing an OAuth-mode connection.
- Keep app shell and apps/shared versions in lockstep; gate OAuth mode on preload capability detection.
- In tests, always inject a stub getGatewayWsUrl for oauth connections.
When it happens
Trigger: Constructing GatewayWsConnection with authMode 'oauth' but omitting deps.getGatewayWsUrl — typically in tests, the web dashboard (which uses a different auth path), or an older Desktop build whose preload lacks the ticket API.
Common situations: Version skew: an updated apps/shared library running inside a stale Electron binary that predates the OAuth ticket bridge; unit tests instantiating the shared client without the desktop dependency.
Related errors
- Your remote gateway session has expired. Open Settings -> Ga
- Could not refresh the remote gateway WebSocket ticket.
- Gateway did not return a WS ticket.
- Reached the gateway over HTTP, but the live WebSocket (/api/
- Hermes backend for profile "${profile}" is HTTP-reachable bu
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/0686ce63237f837e.
Report an issue: GitHub.