decolua/9router · error · Error

"Trae callback missing refreshToken"

Error message

"Trae callback missing refreshToken"

What it means

Thrown by parseTraeCallback when no error param is present but none of the accepted refresh-token keys (refreshToken, refresh_token, RefreshToken) appear in the callback parameters. The callback succeeded from Trae's perspective but the credential this library requires is absent, so the account cannot be persisted.

Source

Thrown at src/lib/oauth/providers/trae.js:99

// Parse the Trae OAuth callback (query string or full URL).
// Expected: ?isRedirect=true&refreshToken=...&loginHost=...[&x-cloudide-token=...]
function parseTraeCallback(raw) {
  const text = String(raw || "").trim();
  let queryStr = text;
  if (text.includes("?")) queryStr = text.slice(text.indexOf("?") + 1);
  if (text.startsWith("#")) queryStr = text.slice(1);
  const params = Object.fromEntries(new URLSearchParams(queryStr));
  const pick = (keys) => {
    for (const k of keys) { const v = params[k]; if (v && String(v).trim()) return String(v).trim(); }
    return null;
  };
  const err = pick(["error", "error_code", "errorCode"]);
  if (err) {
    const desc = pick(["error_description", "error_desc", "message"]);
    throw new Error(desc ? `Trae auth failed: ${err} (${desc})` : `Trae auth failed: ${err}`);
  }
  const refreshToken = pick(["refreshToken", "refresh_token", "RefreshToken"]);
  if (!refreshToken) throw new Error("Trae callback missing refreshToken");
  const loginHost = pick(["loginHost", "login_host", "LoginHost", "host", "consoleHost"]);
  if (!loginHost) throw new Error("Trae callback missing loginHost");
  const cloudideToken = pick(["x-cloudide-token", "xCloudideToken", "accessToken", "access_token", "token"]);
  return { refreshToken, loginHost, cloudideToken };
}

// Allowed API origins for ExchangeToken/GetUserInfo — hardcoded HTTPS allowlist only.
// loginHost from the callback is intentionally NOT honored (SSRF guard: a callback
// attacker could otherwise point this at internal hosts/cloud metadata).
function traeApiOrigins() {
  return [...TRAE_CONFIG.apiOrigins];
}

// POST ExchangeToken {ClientID, RefreshToken, ClientSecret, UserID} → {Result:{AccessToken,RefreshToken,ExpiresAt}}
async function fetchTraeExchangeToken(refreshToken, cloudideToken) {
  const body = JSON.stringify({
    ClientID: TRAE_CONFIG.clientId,
    RefreshToken: refreshToken,

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Inspect the full callback URL/query string and confirm which parameters Trae actually returned.
  2. If Trae renamed the field, add the new key name to the pick() list (refresh_token variants) in parseTraeCallback.
  3. Re-run the login flow and paste the complete, unmodified callback URL.
  4. Check any proxy/redirect layer for query-param stripping.

Example fix

// before
const refreshToken = pick(["refreshToken", "refresh_token", "RefreshToken"]);
// after
const refreshToken = pick(["refreshToken", "refresh_token", "RefreshToken", "refreshTokenKey"]); // add renamed key
Defensive patterns

Strategy: validation

Validate before calling

const params = new URLSearchParams(callbackUrl.split('?')[1] || '');
const hasRefresh = ['refreshToken', 'refresh_token', 'RefreshToken'].some(k => params.get(k)?.trim());
if (!hasRefresh) throw new Error('Callback has no refresh token; re-run Trae login');

Type guard

const isNonEmptyString = (v) => typeof v === 'string' && v.trim().length > 0;

Try / catch

try {
  const creds = parseTraeCallback(callbackUrl);
} catch (e) {
  if (e.message === 'Trae callback missing refreshToken') {
    // log raw query params; re-run login or update accepted key names
  } else throw e;
}

Prevention

When it happens

Trigger: Trae redirects back with a success payload that lacks any refresh-token field: provider changed the callback parameter names, callback was truncated/modified by a redirect chain, or a partial/malformed callback URL was pasted manually.

Common situations: Trae renames callback params in a new version; a middleware/reverse proxy strips query params; the user hand-edited the callback URL; copying the URL dropped part of the query string.

Related errors


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