{"record":{"id":"7f3fad410869117c","repo":"decolua/9router","slug":"cosy-auth-token-is-empty","errorCode":null,"errorMessage":"cosy: auth token is empty","messagePattern":"cosy: auth token is empty","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/shared/qoder/cosy.js","lineNumber":118,"sourceCode":"}\n\n/**\n * Build the full Cosy-* header set for a single Qoder request.\n *\n * @param {Buffer|Uint8Array|string} body  The exact bytes that will be sent.\n *   For GET requests pass an empty Buffer / \"\".\n * @param {string} requestUrl              Full request URL (used for sigPath).\n * @param {object} creds\n * @param {string} creds.userId            Stable Qoder user id.\n * @param {string} creds.authToken         Device access token (`dt-...`).\n * @param {string} [creds.name]            Display name (optional).\n * @param {string} [creds.email]           Email (optional, can be empty).\n * @param {string} [creds.machineId]       Persisted machine UUID.\n * @returns {Record<string, string>} Header map ready to merge onto fetch().\n */\nexport function buildCosyHeaders(body, requestUrl, creds) {\n  if (!creds?.userId) throw new Error(\"cosy: user id is empty\");\n  if (!creds?.authToken) throw new Error(\"cosy: auth token is empty\");\n\n  const bodyBuf = Buffer.isBuffer(body)\n    ? body\n    : typeof body === \"string\"\n      ? Buffer.from(body, \"latin1\")\n      : Buffer.from(body || []);\n\n  const { cosyKey, info } = encryptUserInfo({\n    uid: creds.userId,\n    security_oauth_token: creds.authToken,\n    name: creds.name || \"\",\n    aid: \"\",\n    email: creds.email || \"\",\n  });\n\n  const timestamp = String(Math.floor(Date.now() / 1000));\n  const requestId = uuidv4();\n","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/shared/qoder/cosy.js#L100-L136","documentation":"Companion guard to the userId check: buildCosyHeaders also requires creds.authToken, which is used to authenticate/derive the signed headers. It throws immediately when the token is missing or empty. Without it, requests to Cosy upstreams would fail authentication anyway, so the library fails fast client-side.","triggerScenarios":"Calling execute/headers with creds lacking authToken — e.g. after a login flow that stored userId but no token, a cleared/expired token wiped from storage, or an account record imported without the token field.","commonSituations":"Token revoked upstream and purged locally; token-refresh logic wrote null; account created from a partial config; env/config key renamed so the token is read as undefined.","solutions":["Re-run the Cosy/Qoder login to obtain and store a fresh authToken","Verify the account record's authToken field is populated and non-empty","Check token-refresh code is not writing null/empty on refresh failure","Confirm the credentials object passed in maps the token to creds.authToken"],"exampleFix":"// before\nconst creds = { userId: account.uid, authToken: account.access_token /* undefined after rename */ };\n// after\nconst creds = { userId: account.uid, authToken: account.authToken };\nif (!creds.authToken) await relogin(account);","handlingStrategy":"validation","validationCode":"if (!creds?.authToken) {\n  throw new Error(\"Cosy authToken missing — re-run the Qoder/Cosy login flow\");\n}","typeGuard":"function hasCosyAuthToken(c) { return typeof c?.authToken === \"string\" && c.authToken.length > 0; }","tryCatchPattern":"try {\n  headers = buildCosyHeaders(body, url, creds);\n} catch (e) {\n  if (/auth token is empty/.test(e.message)) {\n    creds = await refreshOrReloginCosy(account);\n    headers = buildCosyHeaders(body, url, creds);\n  } else throw e;\n}","preventionTips":["Never write null/empty on token-refresh failure — keep the old value and flag re-auth","Check authToken presence before executing Cosy requests","Re-run login flow when the token is cleared","Map the stored token field to creds.authToken explicitly"],"tags":["auth","qoder","missing-credential","token"],"backgroundTag":"missing-credential-field","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}