decolua/9router · error · Error

`Windsurf ${path} invalid JSON`

Error message

`Windsurf ${path} invalid JSON`

What it means

After a 2xx response, windsurfSeatRequest parses the body with JSON.parse and throws this if the body is not valid JSON. It exists because Windsurf endpoints can return HTML login/error pages or empty bodies with a 200 status. The message includes the request path so you know which endpoint misbehaved.

Source

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

// ───────────────────────────────────────────────────────────────────────────
// Windsurf OAuth helpers
// ───────────────────────────────────────────────────────────────────────────

async function windsurfSeatRequest(baseUrl, path, body) {
  const url = `${baseUrl.replace(/\/$/, "")}${path}`;
  const res = await fetch(url, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
      "User-Agent": WINDSURF_CONFIG.userAgent,
    },
    body: JSON.stringify(body),
  });
  const text = await res.text();
  if (!res.ok) throw new Error(`Windsurf ${path} HTTP ${res.status}: ${text.slice(0, 200)}`);
  try { return JSON.parse(text); } catch { throw new Error(`Windsurf ${path} invalid JSON`); }
}

// 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}`);
  }

View on GitHub (pinned to 90b52e06ff)

Solutions

  1. Log/inspect the raw response body (temporarily wrap the fetch) to see what is actually returned
  2. Confirm WINDSURF_CONFIG endpoint paths and base URLs are current (paths changed upstream)
  3. Bypass proxies/VPN or disable TLS-intercepting middleboxes during OAuth
  4. Retry — transient gateway issues can yield truncated bodies
  5. Update the library / Windsurf provider config to the latest endpoint definitions

Example fix

// before
const data = await windsurfSeatRequest(apiServerUrl, WINDSURF_CONFIG.registerPath, body);
// after: distinguish upstream HTML from real protocol errors
let data;
try { data = await windsurfSeatRequest(apiServerUrl, WINDSURF_CONFIG.registerPath, body); }
catch (e) {
  if (e.message.includes('invalid JSON')) throw new Error('Windsurf endpoint returned non-JSON (proxy/SSO redirect?) — check network');
  throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// check whether the Windsurf host is being intercepted before the flow
const head = await fetch(baseUrl, { method: 'HEAD' });
const ct = head.headers.get('content-type') || '';
if (ct.includes('text/html')) console.warn('Proxy/portal intercepting Windsurf traffic — expect invalid JSON');

Type guard

const isInvalidJsonError = (e) => e instanceof Error && e.message.includes('invalid JSON');

Try / catch

try { data = await windsurfSeatRequest(url, path, body); }
catch (e) {
  if (isInvalidJsonError(e)) throw new Error('Windsurf returned non-JSON — check proxy/VPN or endpoint path: ' + e.message);
  throw e;
}

Prevention

When it happens

Trigger: A Windsurf endpoint (RegisterUser, GetOneTimeAuthToken, GetCurrentUser) returns 200 with an HTML page, empty string, or otherwise non-JSON body — typically behind a captive portal/SSO redirect, Cloudflare challenge, or after an endpoint path changed.

Common situations: Corporate proxies rewriting responses; hitting a Cloudflare challenge page; API host migrated so the old path serves HTML; truncated response on flaky networks.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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