NousResearch/hermes-agent · error · Error
Loopback callback missing authorization code
Error message
Loopback callback missing authorization code
What it means
In parseLoopbackCallback, the loopback redirect arrived without an 'error' param but also without a 'code' param. The authorization-code half of the OAuth exchange is impossible without the code, so the parse fails loudly rather than proceeding with an empty code. Usually means the redirect came from something other than a successful authorize response (a login page bounce, a partial redirect, or a stray request hitting the loopback port).
Source
Thrown at apps/desktop/electron/native-oauth.ts:161
* `expectedState` MUST match (CSRF defense — RFC 6749 §10.12); a mismatch
* throws rather than proceeding.
*/
export function parseLoopbackCallback(requestUrl: string, expectedState: string): { code: string } {
// requestUrl is the path+query the loopback server received, e.g.
// "/callback?code=...&state=...". Resolve against a dummy origin to parse.
const parsed = new URL(requestUrl, 'http://127.0.0.1')
const error = parsed.searchParams.get('error')
if (error) {
const desc = parsed.searchParams.get('error_description') || ''
throw new Error(`Gateway rejected native login: ${error}${desc ? ` (${desc})` : ''}`)
}
const code = parsed.searchParams.get('code') || ''
const state = parsed.searchParams.get('state') || ''
if (!code) {
throw new Error('Loopback callback missing authorization code')
}
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 || '')View on GitHub (pinned to c896c09c42)
Solutions
- Log the full requestUrl that triggered the parse — stray paths like /favicon.ico explain most cases and can be ignored rather than failed
- Retry the login; a one-off stray request to the loopback port does not indicate broken credentials
- If reproducible every time, capture the gateway's redirect Location header and check why 'code' is missing (authorizer misroute, proxy stripping query)
Example fix
// before
const { code } = parseLoopbackCallback(req.url, expectedState)
// after
if (new URL(req.url, 'http://127.0.0.1').pathname !== '/callback') return // ignore favicon/probes
const { code } = parseLoopbackCallback(req.url, expectedState) Defensive patterns
Strategy: try-catch
Validate before calling
// Ignore non-callback requests hitting the loopback listener
const u = new URL(requestUrl, 'http://127.0.0.1')
if (u.pathname !== '/callback') return // favicon.ico, probes, etc.
const { code } = parseLoopbackCallback(requestUrl, expectedState) Type guard
function looksLikeAuthCallback(requestUrl: string): boolean {
const u = new URL(requestUrl, 'http://127.0.0.1')
return u.pathname === '/callback' && (u.searchParams.has('code') || u.searchParams.has('error'))
} Try / catch
try { const { code } = parseLoopbackCallback(requestUrl, expectedState) } catch (e) { if (e instanceof Error && e.message === 'Loopback callback missing authorization code' && !looksLikeAuthCallback(requestUrl)) return // ignore stray request; keep listening
throw e } Prevention
- Filter loopback listener requests by path before parsing them as OAuth callbacks
- Treat a single stray missing-code request as ignorable; only fail when the real redirect lacks the code
- Retry the login after transient parse failures
When it happens
Trigger: A GET to the loopback callback URL carrying neither code nor error — e.g. a favicon.ico request from the browser, an aborted login redirecting early, a health-check/probe hitting the loopback port, or a gateway redirect that dropped the query string.
Common situations: Browsers auto-requesting /favicon.ico on the loopback redirect page; security software probing the briefly-open loopback listener; gateway misconfiguration stripping query params on redirect.
Related errors
- Gateway rejected native login: ${error}${desc ? ` (${desc})`
- Gateway token response missing access_token
- Loopback callback state mismatch (possible CSRF)
- Branch name is required.
- Missing URL
AI-assisted analysis of NousResearch/hermes-agent@c896c09c42 (2026-08-14).
Data as JSON: /api/errors/aa44ade1778ca408.
Report an issue: GitHub.