{"record":{"id":"cf486bd2e2870249","repo":"decolua/9router","slug":"cosy-user-id-is-empty","errorCode":null,"errorMessage":"cosy: user id is empty","messagePattern":"cosy: user id is empty","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"open-sse/shared/qoder/cosy.js","lineNumber":117,"sourceCode":"  return uuidv4();\n}\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();","sourceCodeStart":99,"sourceCodeEnd":135,"githubUrl":"https://github.com/decolua/9router/blob/90b52e06ffd666b7929554211474d01588f6b1f8/open-sse/shared/qoder/cosy.js#L99-L135","documentation":"buildCosyHeaders signs every Cosy/Qoder request with credentials that must include a userId. The function throws immediately when creds.userId is missing or empty, before computing the encrypted header payload. This is a credential-completeness guard: signed requests cannot be constructed without a user identity.","triggerScenarios":"Executing a Cosy request (via execute or headers helpers) with creds = { authToken: '...' } but no userId, or userId = '' after a partial login/parse of the account record.","commonSituations":"OAuth/login flow stored the authToken but failed to persist userId; account imported by pasting only a token; DB record fields renamed so userId is read from the wrong key; migration dropping the column.","solutions":["Complete the Cosy login flow so both userId and authToken are persisted on the account","Inspect the stored account record and backfill the missing userId","Re-authenticate the Qoder/Cosy provider from the dashboard to rewrite credentials","Check field naming — ensure the code reads creds.userId and the storage layer uses the same key"],"exampleFix":"// before\nconst headers = buildCosyHeaders(body, url, { authToken: account.token });\n// after\nconst headers = buildCosyHeaders(body, url, { authToken: account.token, userId: account.userId });","handlingStrategy":"validation","validationCode":"function canBuildCosyHeaders(creds) {\n  return Boolean(creds?.userId && creds?.authToken);\n}\nif (!canBuildCosyHeaders(creds)) await ensureCosyLogin(account);","typeGuard":"function hasCosyUserId(c) { return typeof c?.userId === \"string\" && c.userId.length > 0; }","tryCatchPattern":"try {\n  headers = buildCosyHeaders(body, url, creds);\n} catch (e) {\n  if (/user id is empty/.test(e.message)) {\n    await reloginCosy(account);\n    headers = buildCosyHeaders(body, url, refreshedCreds);\n  } else throw e;\n}","preventionTips":["Persist userId together with authToken in one transaction","Validate both fields before every Cosy request","Re-auth when either field is missing or empty","Keep field names consistent between storage and creds mapping"],"tags":["auth","qoder","missing-credential","validation"],"backgroundTag":"missing-credential-field","analyzedSha":"90b52e06ffd666b7929554211474d01588f6b1f8","analyzedAt":"2026-08-30T21:05:45.952Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}