{"record":{"id":"92f3424dcb0a478b","repo":"decolua/9router","slug":"no-json-found-in-decoded-code","errorCode":null,"errorMessage":"\"No JSON found in decoded code\"","messagePattern":"\"No JSON found in decoded code\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"info","filePath":"src/lib/oauth/providers/cline.js","lineNumber":22,"sourceCode":"  config: CLINE_CONFIG,\n  flowType: \"authorization_code\",\n  buildAuthUrl: (config, redirectUri) => {\n    const params = new URLSearchParams({\n      client_type: \"extension\",\n      callback_url: redirectUri,\n      redirect_uri: redirectUri,\n    });\n    return `${config.authorizeUrl}?${params.toString()}`;\n  },\n  exchangeToken: async (config, code, redirectUri) => {\n    try {\n      // Cline encodes token data as base64 in the code param\n      let base64 = code;\n      const padding = 4 - (base64.length % 4);\n      if (padding !== 4) base64 += \"=\".repeat(padding);\n      const decoded = Buffer.from(base64, \"base64\").toString(\"utf-8\");\n      const lastBrace = decoded.lastIndexOf(\"}\");\n      if (lastBrace === -1) throw new Error(\"No JSON found in decoded code\");\n      const tokenData = JSON.parse(decoded.substring(0, lastBrace + 1));\n      return {\n        access_token: tokenData.accessToken,\n        refresh_token: tokenData.refreshToken,\n        email: tokenData.email,\n        firstName: tokenData.firstName,\n        lastName: tokenData.lastName,\n        expires_at: tokenData.expiresAt,\n      };\n    } catch (e) {\n      const response = await fetch(config.tokenExchangeUrl, {\n        method: \"POST\",\n        headers: { \"Content-Type\": \"application/json\", Accept: \"application/json\" },\n        body: JSON.stringify({ grant_type: \"authorization_code\", code, client_type: \"extension\", redirect_uri: redirectUri }),\n      });\n      if (!response.ok) {\n        const error = await response.text();\n        throw new Error(`Cline token exchange failed: ${error}`);","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/providers/cline.js#L4-L40","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"null","handlingStrategy":"try-catch","validationCode":"function looksLikeBase64Json(code) {\n  if (typeof code !== 'string' || !code) return false;\n  let b = code;\n  const pad = 4 - (b.length % 4);\n  if (pad !== 4) b += '='.repeat(pad);\n  try {\n    return Buffer.from(b, 'base64').toString('utf-8').includes('}');\n  } catch { return false; }\n}\n// skip the decode path when looksLikeBase64Json(code) is false","typeGuard":"function isEncodedTokenPayload(code) {\n  if (typeof code !== 'string') return false;\n  try {\n    const decoded = Buffer.from(code, 'base64').toString('utf-8');\n    return decoded.includes('{') && decoded.includes('}');\n  } catch { return false; }\n}","tryCatchPattern":"try {\n  const tokens = await cline.exchangeToken(config, code, redirectUri);\n  // use tokens\n} catch (err) {\n  if (err.message === 'No JSON found in decoded code') {\n    // expected when Cline issues plain codes — fallback exchange already ran;\n    // reaching here means BOTH paths failed: surface the fallback error instead\n  }\n  throw err;\n}","preventionTips":["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."],"tags":["oauth","cline","base64","fallback"],"backgroundTag":"no-json-in-decoded-code","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}