NousResearch/hermes-agent · error · Error
Gateway token response missing access_token
Error message
Gateway token response missing access_token
What it means
parseTokenResponse normalizes the JSON from the gateway's /auth/native/token (or refresh) endpoint into a NativeTokenSet, validating the shape so a malformed response fails loudly instead of storing junk credentials. A missing/empty access_token in the body throws this error. It means the token exchange endpoint answered 200-ish JSON without a usable token — typically a gateway version that doesn't implement the native token endpoint, or an error payload being parsed as success.
Source
Thrown at apps/desktop/electron/native-oauth.ts:182
if (!expectedState || state !== expectedState) {
// Never redeem a code that arrived with a mismatched state — it may be a
// forged callback trying to inject an attacker's code.
throw new Error('Loopback callback state mismatch (possible CSRF)')
}
return { code }
}
/**
* Normalize a `/auth/native/token` (or refresh) JSON response into a
* NativeTokenSet, validating the shape. Throws on a missing/short access
* token so a malformed response fails loudly rather than storing junk.
*/
export function parseTokenResponse(body: any): NativeTokenSet {
const accessToken = String(body?.access_token || '')
if (!accessToken) {
throw new Error('Gateway token response missing access_token')
}
const expiresAt = Number(body?.expires_at)
return {
accessToken,
refreshToken: String(body?.refresh_token || ''),
expiresAt: Number.isFinite(expiresAt) ? expiresAt : 0,
provider: String(body?.provider || ''),
userId: String(body?.user_id || '')
}
}
/**
* Validate a token set loaded from the encrypted local store.
*
* The stored representation is already normalized as NativeTokenSet and
* therefore uses camelCase. Gateway token responses use snake_case andView on GitHub (pinned to c896c09c42)
Solutions
- Log the raw response body — it usually contains the real reason (error field, HTML error page) the parser only sees as missing token
- Update the hermes runtime/gateway to a version implementing /auth/native/token
- Confirm the authorization code is fresh (single-use) and the flow wasn't retried with the same code
- If a proxy sits in front of the gateway, bypass it to check whether it mangles the token response
Defensive patterns
Strategy: try-catch
Validate before calling
// Sanity-check the exchange response before parsing
const res = await fetch(tokenEndpoint, { method: 'POST', body })
const body = await res.json().catch(() => null)
if (!res.ok || !body?.access_token) throw new Error(`Token exchange failed (${res.status}): ${JSON.stringify(body)}`)
const tokens = parseTokenResponse(body) Type guard
function isTokenResponseShape(body: unknown): body is { access_token: string } {
return typeof (body as any)?.access_token === 'string' && (body as any).access_token.length > 0
} Try / catch
try { const tokens = parseTokenResponse(body) } catch (e) { if (e instanceof Error && e.message === 'Gateway token response missing access_token') { logRawBody(body); checkGatewayVersion(); throw new Error('Gateway did not return a native token — update the hermes runtime?') } throw e } Prevention
- Check HTTP status and log the raw body before parsing the token response
- Keep desktop app and hermes gateway versions aligned (native /auth/native/token support)
- Never retry the exchange with an already-consumed authorization code
When it happens
Trigger: POST /auth/native/token (or /auth/native/refresh) returns JSON lacking access_token — older gateway without native-flow support, an error envelope {error: ...} with HTTP 200, a proxy rewriting the response, or a code already redeemed/expired so the gateway returns an error body.
Common situations: Desktop app newer than the deployed hermes runtime (native flow absent); gateway behind an API gateway that swallows the token response; retrying the exchange with a consumed authorization code.
Related errors
- Remote gateway session token is required.
- Gateway rejected native login: ${error}${desc ? ` (${desc})`
- Loopback callback missing authorization code
- Remote Hermes gateway is selected, but no session token is s
- HERMES_DESKTOP_REMOTE_URL is set but HERMES_DESKTOP_REMOTE_T
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/c896a8c33e7c6412.
Report an issue: GitHub.