NousResearch/hermes-agent · error · Error
Gateway did not return a WS ticket.
Error message
Gateway did not return a WS ticket.
What it means
First of two identical guards in the WS-ticket minting helper: when a native access token was obtained (ensureNativeAccessToken succeeded) and POST {baseUrl}/api/auth/ws-ticket returned a body whose `ticket` is missing or not a string. The request itself succeeded (fetchJson resolved) — the response shape is wrong, typically an error payload or auth rejection that still returned 200/JSON.
Source
Thrown at apps/desktop/electron/main.ts:6341
// Prefers a native bearer token (cookieless RFC 8252 flow) when present,
// falling back to the OAuth cookie partition otherwise.
// Throws (with statusCode 401) if the session cookie is missing/expired —
// callers treat that as "needs re-login".
async function mintGatewayWsTicket(baseUrl) {
// Native flow: mint the ticket with the bearer token, no cookie involved.
const nativeAt = await ensureNativeAccessToken(baseUrl).catch(() => null)
if (nativeAt) {
const body = (await fetchJson(`${baseUrl}/api/auth/ws-ticket`, null, {
method: 'POST',
timeoutMs: 8_000,
bearer: nativeAt
})) as any
const ticket = body?.ticket
if (!ticket || typeof ticket !== 'string') {
throw new Error('Gateway did not return a WS ticket.')
}
return ticket
}
const body = (await fetchJsonViaOauthSession(`${baseUrl}/api/auth/ws-ticket`, {
method: 'POST',
timeoutMs: 8_000
})) as any
const ticket = body?.ticket
if (!ticket || typeof ticket !== 'string') {
throw new Error('Gateway did not return a WS ticket.')
}
return ticket
}View on GitHub (pinned to c896c09c42)
Solutions
- Update the Hermes backend so /api/auth/ws-ticket exists and returns {ticket: string}.
- Re-authenticate: the native access token may be stale — force a re-login and retry ticket minting.
- Inspect the actual response body (log it) to see what the gateway returned instead of a ticket.
- Bypass proxies that may rewrite the response when testing locally.
Defensive patterns
Strategy: try-catch
Type guard
function isWsTicketResponse(body) {
return body != null && typeof body.ticket === 'string' && body.ticket.length > 0
} Try / catch
try {
const ticket = await mintWsTicket(baseUrl)
} catch (e) {
if (/did not return a WS ticket/.test(e.message)) {
await reAuthenticateAndRetry() // stale native token or old gateway
} else throw e
} Prevention
- Keep the gateway backend updated alongside the app
- Refresh the native access token on auth-shaped failures
- Log response bodies when the ticket shape mismatches
When it happens
Trigger: Gateway version that predates the /api/auth/ws-ticket endpoint returning some other JSON; token valid enough to avoid a fetch error but rejected by the endpoint, returning {error: ...}; a proxy in front of the gateway rewriting the response.
Common situations: Desktop app newer than the gateway backend (endpoint missing); expired-but-parseable auth responses; reverse proxies injecting a JSON body.
Related errors
- Reached the gateway over HTTP, but the live WebSocket (/api/
- Hermes backend for profile "${profile}" is HTTP-reachable bu
- Local Hermes backend is HTTP-reachable but the WebSocket (/a
- 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/2bd4c58d4d703541.
Report an issue: GitHub.