coleam00/Archon · error

OpenAI token ${operation} failed (${response.status})${error

Error message

OpenAI token ${operation} failed (${response.status})${errorCode ? `: ${errorCode}` : ''}

What it means

postTokenRequest (openai-oauth.ts:201) throws when the OpenAI token endpoint returns a non-2xx status. The message carries the HTTP status plus, when parseable, the OAuth `error` code extracted from a JSON body — the raw body is deliberately stripped because it can carry account identifiers. This is the server-side rejection of the exchange or refresh request.

Source

Thrown at packages/core/src/credentials/openai-oauth.ts:201

  }
  if (!response.ok) {
    // Strip the error body down to the OAuth `error` code: this message flows
    // into the bridge's session.detail (and on to the browser/CLI), and OpenAI
    // error bodies can carry account identifiers. Never include the raw body.
    const text = await response.text().catch(() => '');
    let errorCode = '';
    try {
      const parsed = JSON.parse(text) as { error?: unknown };
      if (typeof parsed.error === 'string') {
        errorCode = parsed.error;
      } else if (parsed.error && typeof parsed.error === 'object') {
        const code = (parsed.error as { code?: unknown }).code;
        if (typeof code === 'string') errorCode = code;
      }
    } catch {
      // Non-JSON error body — drop it entirely; the status code must suffice.
    }
    throw new Error(
      `OpenAI token ${operation} failed (${response.status})${errorCode ? `: ${errorCode}` : ''}`
    );
  }
  let raw: unknown;
  try {
    raw = await response.json();
  } catch {
    // An HTTP 200 with a non-JSON body (proxy/maintenance page) must surface
    // as a labeled error, not a raw SyntaxError mistaken for an Archon bug.
    throw new Error(
      `OpenAI token ${operation} returned a non-JSON response (HTTP ${response.status}).`
    );
  }
  return raw as OpenAiTokenResponse;
}

/**
 * Map a token response onto the stored credential blob. Fails loud on a

View on GitHub (pinned to 0773b97458)

Solutions

  1. For invalid_grant on exchange: start a fresh login (new authorize URL) — authorization codes are single-use and short-lived.
  2. For refresh failures (401/invalid_grant): re-run the subscription login to obtain new tokens.
  3. Check the status + error code in the message for the precise OAuth failure reason.
  4. If 5xx, wait and retry; consult OpenAI status for incidents.

Example fix

// before
refreshTokens(staleRefreshToken); // 400 invalid_grant
// after
await startOAuth(userId, 'openai'); // fresh login obtains new refresh token
Defensive patterns

Strategy: validation

Validate before calling

// Validate the pasted authorization input early — codes are single-use and short-lived
const parsed = parseOpenAiAuthorizationInput(pastedValue);
if (!parsed.code || (parsed.state && parsed.state !== flow.state)) {
  throw new Error('Paste a fresh redirect URL/code from the current login attempt.');
}

Try / catch

try {
  await refreshToken(refreshToken);
} catch (e) {
  const m = e instanceof Error ? e.message : '';
  if (/\(401\)|invalid_grant/.test(m)) {
    // Refresh token revoked/expired: full re-login required
    return startOAuth(userId, 'openai');
  }
  if (/\(5\d\d\)/.test(m)) {
    return retryWithBackoff(() => refreshToken(refreshToken));
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling exchangeOpenAiAuthorizationCode with an invalid/expired/already-used authorization code (400 invalid_grant), or refreshing with a revoked/expired refresh_token (401), or any 4xx/5xx from the token endpoint.

Common situations: User pasting a stale redirect URL after retrying the authorize flow (code single-use); refresh token rotated/revoked server-side (user logged out all devices); clock skew invalidating codes; OpenAI returning 5xx during an incident.

Related errors


AI-assisted analysis of coleam00/Archon@0773b97458 (2026-09-01). Data as JSON: /api/errors/faa8a97dbe2b5568. Report an issue: GitHub.