{"record":{"id":"b9640fa45fd9bdb1","repo":"decolua/9router","slug":"windsurf-registeruser-missing-apikey","errorCode":null,"errorMessage":"Windsurf RegisterUser missing apiKey","messagePattern":"Windsurf RegisterUser missing apiKey","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/lib/oauth/providers/windsurf.js","lineNumber":55,"sourceCode":"    const desc = pick([\"error_description\"]);\n    throw new Error(desc ? `Windsurf auth failed: ${err} (${desc})` : `Windsurf auth failed: ${err}`);\n  }\n  const accessToken = pick([\"access_token\", \"token\"]);\n  if (!accessToken) throw new Error(\"Windsurf callback missing access_token\");\n  const state = pick([\"state\"]);\n  if (expectedState && state && state !== expectedState) {\n    throw new Error(\"Windsurf callback state mismatch\");\n  }\n  return { firebaseIdToken: accessToken };\n}\n\n// POST RegisterUser {firebase_id_token} → {apiKey, apiServerUrl, name}\nasync function fetchWindsurfRegisterUser(firebaseIdToken) {\n  const data = await windsurfSeatRequest(WINDSURF_CONFIG.registerApiBaseUrl, WINDSURF_CONFIG.registerPath, {\n    firebase_id_token: firebaseIdToken,\n  });\n  const apiKey = extractJsonPath(data, [[\"apiKey\"], [\"api_key\"]]);\n  if (!apiKey) throw new Error(\"Windsurf RegisterUser missing apiKey\");\n  const apiServerUrl = extractJsonPath(data, [[\"apiServerUrl\"], [\"api_server_url\"]]) || WINDSURF_CONFIG.defaultApiServerUrl;\n  const name = extractJsonPath(data, [[\"name\"]]);\n  return { apiKey, apiServerUrl, name };\n}\n\n// Best-effort: GetOneTimeAuthToken → GetCurrentUser → email/name.\nasync function fetchWindsurfUserInfo(apiServerUrl, firebaseIdToken) {\n  try {\n    const authRes = await windsurfSeatRequest(apiServerUrl, WINDSURF_CONFIG.oneTimeAuthPath, { firebaseIdToken });\n    const authToken = extractJsonPath(authRes, [[\"authToken\"], [\"auth_token\"]]);\n    if (!authToken) return { email: null, name: null };\n    const userRes = await windsurfSeatRequest(apiServerUrl, WINDSURF_CONFIG.currentUserPath, {\n      authToken,\n      includeSubscription: true,\n    });\n    const user = userRes.user || userRes;\n    return {\n      email: extractJsonPath(user, [[\"email\"]]),","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/src/lib/oauth/providers/windsurf.js#L37-L73","documentation":"fetchWindsurfRegisterUser POSTs the Firebase id token to Windsurf's RegisterUser endpoint and expects {apiKey, ...} (or api_key). It throws this when the response parsed fine as JSON but contains no API key under either key. The upstream accepted the request but did not return a credential — usually an upstream response-shape change or a soft business-logic rejection.","triggerScenarios":"Windsurf RegisterUser returns 200 with an error object like {success:false,...} or {message:\"...\"}; response wrapped differently (e.g. {data:{apiKey}}) after an API version change; account not provisioned so no key is issued.","commonSituations":"Windsurf changed the RegisterUser response schema; the user's account exists but has no seat so no key is minted; account region routed to an API version with different field casing.","solutions":["Log the full `data` object returned by RegisterUser to see the actual response shape","Update the extractJsonPath key list in fetchWindsurfRegisterUser if Windsurf renamed/nested the field","Confirm the Windsurf account has an active seat/subscription — no key is issued without one","Re-run the OAuth flow to get a fresh firebase_id_token and retry","Check for library updates that track the current Windsurf API"],"exampleFix":"// before\nconst apiKey = extractJsonPath(data, [[\"apiKey\"], [\"api_key\"]]);\n// after: also probe nested shapes\nconst apiKey = extractJsonPath(data, [[\"apiKey\"], [\"api_key\"], [\"data\",\"apiKey\"], [\"result\",\"apiKey\"]]);","handlingStrategy":"type-guard","validationCode":"// confirm the firebase token is JWT-shaped before RegisterUser\nconst jwtShaped = (t) => typeof t === 'string' && t.trim().split('.').length === 3;\nif (!jwtShaped(firebaseIdToken)) throw new Error('Not a Firebase id token — run the Windsurf sign-in flow');","typeGuard":"const hasApiKey = (data) =>\n  Boolean(data && (data.apiKey || data.api_key ||\n    (data.data && data.data.apiKey) || (data.result && data.result.apiKey)));","tryCatchPattern":"const reg = await fetchWindsurfRegisterUser(fb);\nif (!reg || !reg.apiKey) {\n  throw new Error('Windsurf issued no apiKey — check account seat/subscription or updated response shape');\n}","preventionTips":["Log the full RegisterUser JSON when onboarding fails — schema drift is the usual cause","Keep extractJsonPath key lists updated with any new field casing/nesting","Verify the account has an active Windsurf seat before automating onboarding","Monitor library updates that track Windsurf API changes"],"tags":["oauth","schema-change","windsurf","missing-field"],"backgroundTag":"upstream-response-schema-changed","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}