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
- Log/inspect the raw response body (temporarily wrap the fetch) to see what is actually returned
- Confirm WINDSURF_CONFIG endpoint paths and base URLs are current (paths changed upstream)
- Bypass proxies/VPN or disable TLS-intercepting middleboxes during OAuth
- Retry — transient gateway issues can yield truncated bodies
- 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
- Disable TLS-intercepting proxies/VPNs during OAuth flows
- Verify endpoint paths against the current Windsurf API after library updates
- Log the raw response body when debugging to spot HTML pages
- Retry once on transient truncation before failing the flow
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- `Windsurf ${path} HTTP ${res.status}: ${text.slice(0, 200)}`
- desc ? `Windsurf auth failed: ${err} (${desc})` : `Windsurf
- Windsurf callback missing access_token
- Windsurf callback state mismatch
- Windsurf RegisterUser missing apiKey
AI-assisted analysis of decolua/9router@90b52e06ff (2026-08-30).
Data as JSON: /api/errors/91cd8484915d27fc.
Report an issue: GitHub.