{"record":{"id":"0686ce63237f837e","repo":"NousResearch/hermes-agent","slug":"this-desktop-build-cannot-refresh-oauth-websocket","errorCode":null,"errorMessage":"This Desktop build cannot refresh OAuth WebSocket tickets. Update Hermes Desktop and try again.","messagePattern":"This Desktop build cannot refresh OAuth WebSocket tickets\\. Update Hermes Desktop and try again\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"apps/shared/src/websocket-url.ts","lineNumber":45,"sourceCode":"    super(message, options)\n    this.name = 'GatewayReauthRequiredError'\n  }\n}\n\nexport function isGatewayReauthRequired(error: unknown): error is GatewayReauthRequiredError {\n  return (\n    error instanceof GatewayReauthRequiredError ||\n    (typeof error === 'object' && error !== null && (error as { needsOauthLogin?: unknown }).needsOauthLogin === true)\n  )\n}\n\nexport async function resolveGatewayWsUrl(deps: ResolveGatewayWsUrlDeps, conn: GatewayWsConnection): Promise<string> {\n  const mint = deps.getGatewayWsUrl\n  const profile = conn.profile ?? null\n\n  if (conn.authMode === 'oauth') {\n    if (!mint) {\n      throw new Error('This Desktop build cannot refresh OAuth WebSocket tickets. Update Hermes Desktop and try again.')\n    }\n\n    try {\n      const result = await mint(profile)\n\n      if (typeof result === 'string') {\n        return result\n      }\n\n      if (result.ok) {\n        return result.wsUrl\n      }\n\n      if (result.needsOauthLogin) {\n        throw new GatewayReauthRequiredError(\n          'Your remote gateway session has expired. Open Settings -> Gateway and click \"Sign in\" again.',\n          { cause: new Error(result.error) }\n        )","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/apps/shared/src/websocket-url.ts#L27-L63","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nresolveGatewayWsUrl({} as deps, conn) // oauth conn, no mint\n\n// after\nresolveGatewayWsUrl({ getGatewayWsUrl: (profile) => hermesDesktop.gateway.wsUrl(profile) }, conn)","handlingStrategy":"validation","validationCode":"function canMintOAuthTickets(deps: ResolveGatewayWsUrlDeps): boolean {\n  return typeof deps.getGatewayWsUrl === 'function'\n}\n\n// before constructing: if conn.authMode === 'oauth', require the mint dep","typeGuard":"function hasMintDep(deps: ResolveGatewayWsUrlDeps): deps is ResolveGatewayWsUrlDeps & {\n  getGatewayWsUrl: (profile: null | string) => Promise<string | { ok: true; wsUrl: string } | { ok: false; needsOauthLogin?: boolean; error?: string }>\n} {\n  return typeof deps.getGatewayWsUrl === 'function'\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["desktop","oauth","websocket","version-skew","shared"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}