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

  1. Log the full requestUrl that triggered the parse — stray paths like /favicon.ico explain most cases and can be ignored rather than failed
  2. Retry the login; a one-off stray request to the loopback port does not indicate broken credentials
  3. 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

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


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