decolua/9router · error · Error

Windsurf callback missing access_token

Error message

Windsurf callback missing access_token

What it means

parseWindsurfCallback throws this when the callback URL contains no error but also no access_token (or token) parameter, so there is no Firebase id token to continue with. The Windsurf flow uses response_type=token (implicit flow), so the token must arrive in the redirect fragment/query; its absence means the redirect was not a successful token callback.

Source

Thrown at src/lib/oauth/providers/windsurf.js:41

// Parse Windsurf callback (query string or full URL): ?access_token=...&state=...
function parseWindsurfCallback(raw, expectedState) {
  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"]);
  if (err) {
    const desc = pick(["error_description"]);
    throw new Error(desc ? `Windsurf auth failed: ${err} (${desc})` : `Windsurf auth failed: ${err}`);
  }
  const accessToken = pick(["access_token", "token"]);
  if (!accessToken) throw new Error("Windsurf callback missing access_token");
  const state = pick(["state"]);
  if (expectedState && state && state !== expectedState) {
    throw new Error("Windsurf callback state mismatch");
  }
  return { firebaseIdToken: accessToken };
}

// POST RegisterUser {firebase_id_token} → {apiKey, apiServerUrl, name}
async function fetchWindsurfRegisterUser(firebaseIdToken) {
  const data = await windsurfSeatRequest(WINDSURF_CONFIG.registerApiBaseUrl, WINDSURF_CONFIG.registerPath, {
    firebase_id_token: firebaseIdToken,
  });
  const apiKey = extractJsonPath(data, [["apiKey"], ["api_key"]]);
  if (!apiKey) throw new Error("Windsurf RegisterUser missing apiKey");
  const apiServerUrl = extractJsonPath(data, [["apiServerUrl"], ["api_server_url"]]) || WINDSURF_CONFIG.defaultApiServerUrl;
  const name = extractJsonPath(data, [["name"]]);
  return { apiKey, apiServerUrl, name };
}

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Ensure the user copies the FULL final URL from the browser address bar, including the #access_token=... fragment
  2. Confirm the local callback server (callbackPath) captured the fragment before redirecting — fragments only exist client-side
  3. Verify buildAuthUrl uses response_type=token and redirect_parameters_type=query so Windsurf appends the token
  4. Check the raw string is non-empty and is the redirect URL, not the auth page URL
  5. Retry the OAuth flow in a normal (non-embedded) browser

Example fix

// before: caller passes whatever the user typed
const tokens = await exchangeToken(config, rawPastedUrl, redirectUri, null, state);
// after: sanity-check before calling
if (!/(access_token=|token=)/.test(rawPastedUrl)) {
  throw new Error('Pasted URL has no access_token — copy the full redirect URL including the # fragment');
}
const tokens = await exchangeToken(config, rawPastedUrl, redirectUri, null, state);
Defensive patterns

Strategy: validation

Validate before calling

// validate the pasted/redirected URL contains a token before parsing
const hasToken = (raw) => {
  const s = String(raw || '');
  const q = s.slice(s.indexOf('?') + 1);
  const frag = s.includes('#') ? s.slice(s.indexOf('#') + 1) : '';
  const p = new URLSearchParams(q + (frag ? '&' + frag : ''));
  return Boolean(p.get('access_token') || p.get('token'));
};
if (!hasToken(callbackUrl)) throw new Error('Callback URL has no access_token — copy the FULL final redirect URL');

Type guard

const isTokenCallback = (raw) => /(access_token=|token=)/.test(String(raw || ''));

Try / catch

try { ({ firebaseIdToken } = parseWindsurfCallback(raw, state)); }
catch (e) {
  if (e.message === 'Windsurf callback missing access_token') {
    throw new Error('No token in redirect — re-run sign-in and copy the complete URL including the #fragment');
  }
  throw e;
}

Prevention

When it happens

Trigger: User pastes the wrong URL (e.g. the signin page URL instead of the final redirect); the redirect lost the hash fragment (fragments are not forwarded by intermediate servers); Windsurf redirected to a different page after login; empty/garbage string passed as raw.

Common situations: Manual paste-token flow where the user copies the wrong URL from the browser; redirect middleware stripping query params; hash-based tokens lost because the callback went through an HTTP redirect.

Related errors


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