decolua/9router · error · Error

`Cline token exchange failed: ${error}`

Error message

`Cline token exchange failed: ${error}`

What it means

When the base64-encoded-JSON shortcut in cline.js throws (see error 197), the code falls back to POSTing the authorization code to Cline's tokenExchangeUrl. If that HTTP fallback returns non-ok, this error is thrown with the raw response body. This is the terminal failure of the Cline token exchange — the code could not be converted into tokens either locally or via the API.

Source

Thrown at src/lib/oauth/providers/cline.js:40

      if (lastBrace === -1) throw new Error("No JSON found in decoded code");
      const tokenData = JSON.parse(decoded.substring(0, lastBrace + 1));
      return {
        access_token: tokenData.accessToken,
        refresh_token: tokenData.refreshToken,
        email: tokenData.email,
        firstName: tokenData.firstName,
        lastName: tokenData.lastName,
        expires_at: tokenData.expiresAt,
      };
    } catch (e) {
      const response = await fetch(config.tokenExchangeUrl, {
        method: "POST",
        headers: { "Content-Type": "application/json", Accept: "application/json" },
        body: JSON.stringify({ grant_type: "authorization_code", code, client_type: "extension", redirect_uri: redirectUri }),
      });
      if (!response.ok) {
        const error = await response.text();
        throw new Error(`Cline token exchange failed: ${error}`);
      }
      const data = await response.json();
      return {
        access_token: data.data?.accessToken || data.accessToken,
        refresh_token: data.data?.refreshToken || data.refreshToken,
        email: data.data?.userInfo?.email || "",
        expires_at: data.data?.expiresAt || data.expiresAt,
      };
    }
  },
  mapTokens: (tokens) => ({
    accessToken: tokens.access_token,
    refreshToken: tokens.refresh_token,
    expiresIn: tokens.expires_at
      ? Math.floor((new Date(tokens.expires_at).getTime() - Date.now()) / 1000)
      : 3600,
    email: tokens.email,
    providerSpecificData: { firstName: tokens.firstName, lastName: tokens.lastName },

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Read the appended body for the upstream error (invalid_grant etc.) and fix that cause.
  2. invalid_grant: restart the OAuth flow — codes are single-use and short-lived.
  3. Verify redirect_uri passed to exchangeToken equals the callback_url/redirect_uri used in buildAuthUrl.
  4. Check Cline service status / update tokenExchangeUrl if the API moved.
  5. Retry after a 5xx with a fresh authorization flow.

Example fix

// before: code reused from a previous completed flow
await exchangeToken(config, usedCode, redirectUri); // 400 invalid_grant
// after: always exchange a freshly delivered code exactly once
const tokens = await exchangeToken(config, freshCallbackCode, redirectUri);
Defensive patterns

Strategy: try-catch

Validate before calling

const code = new URL(callbackUrl).searchParams.get('code');
if (!code || code.length < 8) throw new Error('invalid or missing code from Cline callback — skip exchange');
if (usedClineCodes.has(code)) throw new Error('Cline code already exchanged');

Type guard

function isPlausibleClineCode(v) {
  return typeof v === 'string' && v.trim().length >= 8 && !/\s/.test(v);
}

Try / catch

try {
  const tokens = await cline.exchangeToken(config, code, redirectUri);
  // use tokens
} catch (err) {
  if (String(err.message).startsWith('Cline token exchange failed:')) {
    const body = err.message.slice('Cline token exchange failed:'.length);
    if (/invalid_grant|expired/i.test(body)) startFreshClineLogin();
    else if (/5\d\d|unavailable/i.test(body)) scheduleRetryWithBackoff();
    else reportToUser(body); // 4xx config/contract issue
  } else throw err;
}

Prevention

When it happens

Trigger: Fallback exchange with: an expired/replayed authorization code, a redirect_uri not matching the one in buildAuthUrl, Cline API rejecting client_type 'extension' or returning 4xx/5xx, or the code param being garbage (neither base64 JSON nor a valid code).

Common situations: Cline service outage or API contract change, user refreshing the callback (code reuse), wrong callback_url/redirect_uri in CLINE_CONFIG, network proxy returning error pages, or expired codes from slow manual flows.

Related errors


AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30). Data as JSON: /api/errors/8d18f5fda32632e1. Report an issue: GitHub.