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

  1. Update the Hermes backend so /api/auth/ws-ticket exists and returns {ticket: string}.
  2. Re-authenticate: the native access token may be stale — force a re-login and retry ticket minting.
  3. Inspect the actual response body (log it) to see what the gateway returned instead of a ticket.
  4. 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

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


AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14). Data as JSON: /api/errors/2bd4c58d4d703541. Report an issue: GitHub.