decolua/9router · info · Error
"No JSON found in decoded code"
Error message
"No JSON found in decoded code"
What it means
cline.js first assumes the OAuth `code` callback param is itself a base64-encoded JSON blob of token data (the Cline extension-style flow). It base64-decodes the code, finds the last '}' and parses the JSON. This error is thrown when the decoded string contains no '}' — i.e. the code is not an encoded token payload but a plain authorization code. The catch block then falls back to a real HTTP token exchange, so this error is an internal branch trigger, not a user-facing failure, unless the fallback exchange also fails.
Source
Thrown at src/lib/oauth/providers/cline.js:22
config: CLINE_CONFIG,
flowType: "authorization_code",
buildAuthUrl: (config, redirectUri) => {
const params = new URLSearchParams({
client_type: "extension",
callback_url: redirectUri,
redirect_uri: redirectUri,
});
return `${config.authorizeUrl}?${params.toString()}`;
},
exchangeToken: async (config, code, redirectUri) => {
try {
// Cline encodes token data as base64 in the code param
let base64 = code;
const padding = 4 - (base64.length % 4);
if (padding !== 4) base64 += "=".repeat(padding);
const decoded = Buffer.from(base64, "base64").toString("utf-8");
const lastBrace = decoded.lastIndexOf("}");
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}`);View on GitHub (pinned to 90b52e06ff)
Solutions
- No action usually needed: the catch block automatically performs the standard Cline token exchange.
- If both paths fail, check the fallback error (`Cline token exchange failed: ...`) — that is the real cause.
- Verify the callback URL supplied the code param from Cline's authorize endpoint unmodified (not URL-decoded twice).
- Update CLINE_CONFIG/tokenExchangeUrl if Cline changed its extension flow.
Example fix
null
Defensive patterns
Strategy: try-catch
Validate before calling
function looksLikeBase64Json(code) {
if (typeof code !== 'string' || !code) return false;
let b = code;
const pad = 4 - (b.length % 4);
if (pad !== 4) b += '='.repeat(pad);
try {
return Buffer.from(b, 'base64').toString('utf-8').includes('}');
} catch { return false; }
}
// skip the decode path when looksLikeBase64Json(code) is false Type guard
function isEncodedTokenPayload(code) {
if (typeof code !== 'string') return false;
try {
const decoded = Buffer.from(code, 'base64').toString('utf-8');
return decoded.includes('{') && decoded.includes('}');
} catch { return false; }
} Try / catch
try {
const tokens = await cline.exchangeToken(config, code, redirectUri);
// use tokens
} catch (err) {
if (err.message === 'No JSON found in decoded code') {
// expected when Cline issues plain codes — fallback exchange already ran;
// reaching here means BOTH paths failed: surface the fallback error instead
}
throw err;
} Prevention
- Treat this error as an internal branch signal; the code self-recovers via HTTP fallback.
- Only worry when it appears together with 'Cline token exchange failed' — that combo means both paths failed.
- Pass the code param through unmodified (avoid double URL-decoding).
- Pin/verify the exact error message when branching — it is an exact string, not a code.
When it happens
Trigger: exchangeToken receives a plain/short authorization code (e.g. 'abc123') whose base64 decoding contains no JSON, or an empty/garbage code param from a malformed callback.
Common situations: Cline changing its callback format so codes are no longer base64 JSON, calling the flow with a manually pasted code, or a misconfigured redirect that passes error params as the code.
Related errors
- "No JSON found in decoded code"
- `Cline token exchange failed: ${error}`
- [Claude Usage] OAuth endpoint returned ${oauthResponse.statu
- [Codex Reset Credits] force refresh failed: ${retryError.mes
- [Usage] ${connection.provider}: force refresh failed: ${retr
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/92f3424dcb0a478b.
Report an issue: GitHub.