{"record":{"id":"b821b50991f0c4c1","repo":"decolua/9router","slug":"zed-callback-must-include-user-id-and-access-token","errorCode":null,"errorMessage":"Zed callback must include user_id and access_token","messagePattern":"Zed callback must include user_id and access_token","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/shared/zedAuth.js","lineNumber":128,"sourceCode":"    let url;\n    try {\n      url = new URL(raw);\n    } catch {\n      try {\n        url = new URL(`http://127.0.0.1/?${raw.replace(/^\\?/, \"\")}`);\n      } catch {\n        throw new Error(\"Invalid Zed callback URL\");\n      }\n    }\n    url.searchParams.forEach((value, key) => {\n      data[key] = value;\n    });\n  }\n\n  const userId = data.user_id || data.userId;\n  const encryptedAccessToken = data.access_token || data.accessToken || data.token;\n  if (!userId || !encryptedAccessToken) {\n    throw new Error(\"Zed callback must include user_id and access_token\");\n  }\n  return { userId: String(userId), encryptedAccessToken: String(encryptedAccessToken) };\n}\n\n/** Decrypt the RSA-encrypted access token using the stored private key. */\nexport function decryptZedAccessToken(encryptedAccessToken, privateKeyVerifier) {\n  const privateKey = decodeZedPrivateKeyVerifier(privateKeyVerifier);\n  const encrypted = Buffer.from(String(encryptedAccessToken), \"base64url\");\n  try {\n    return crypto\n      .privateDecrypt(\n        { key: privateKey, padding: crypto.constants.RSA_PKCS1_OAEP_PADDING, oaepHash: \"sha256\" },\n        encrypted,\n      )\n      .toString(\"utf8\");\n  } catch (oaepError) {\n    try {\n      return crypto","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/shared/zedAuth.js#L110-L146","documentation":"After parsing the Zed callback payload (JSON or URL query), parseZedCallbackPayload requires both a user id (`user_id`/`userId`) and an encrypted access token (`access_token`/`accessToken`/`token`). If either key is missing or empty it throws this error, because a Zed credential cannot be constructed without both values.","triggerScenarios":"Calling parseZedCallbackPayload with a parseable URL/JSON that lacks user_id or access_token — e.g. the redirect only carried `state`/`code` params, or the JSON contains differently-named keys.","commonSituations":"Zed changed its callback param names, the user pasted the initial authorize URL (which has code/state but no tokens), or the callback returned an error payload like `?error=access_denied` instead of tokens.","solutions":["Inspect the pasted payload: confirm it contains user_id (or userId) AND access_token (or accessToken/token).","Complete the full OAuth flow so the final redirect includes both params; an intermediate redirect won't do.","If building the payload manually, include both fields: {\"user_id\":\"...\",\"access_token\":\"...\"}.","Check for error params in the callback (error/error_description) — if present, re-run sign-in instead of parsing."],"exampleFix":"// before\nparseZedCallbackPayload(\"http://127.0.0.1/?state=xyz\"); // no user_id/access_token\n// after\nparseZedCallbackPayload(\"http://127.0.0.1/?user_id=42&access_token=ENCRYPTED_B64\");","handlingStrategy":"validation","validationCode":"function hasZedCallbackFields(raw) {\n  let data = {};\n  try { data = JSON.parse(raw); }\n  catch { try { new URL(`http://x/?${String(raw).replace(/^\\?/, \"\")}`).searchParams.forEach((v, k) => (data[k] = v)); } catch { return false; } }\n  return Boolean((data.user_id || data.userId) && (data.access_token || data.accessToken || data.token));\n}\n","typeGuard":"const hasZedPayload = (d) => Boolean(d && (d.user_id || d.userId) && (d.access_token || d.accessToken || d.token));","tryCatchPattern":"try {\n  const payload = parseZedCallbackPayload(input);\n} catch (e) {\n  if (String(e.message).includes(\"must include\")) {\n    // show which fields were found vs missing; re-run the OAuth flow\n  } else throw e;\n}","preventionTips":["After OAuth completes, verify the final redirect URL contains both user_id and access_token before parsing.","Check for `error` query params in the callback and abort with the provider's error_description.","When constructing payloads manually, validate both keys exist first."],"tags":["validation","oauth","missing-field"],"backgroundTag":"missing-required-field","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}