{"record":{"id":"a03ebbee9e0330d1","repo":"jackwener/OpenCLI","slug":"zsxq-v2-users-self-200-but-user-id-missing-inco","errorCode":null,"errorMessage":"zsxq /v2/users/self 200 but user_id missing — incomplete session","messagePattern":"zsxq /v2/users/self 200 but user_id missing — incomplete session","errorType":"error_code","errorClass":"AuthRequiredError","httpStatus":null,"severity":"error","filePath":"clis/zsxq/auth.js","lineNumber":41,"sourceCode":"        }\n        if (!r.ok) return { kind: 'http', httpStatus: r.status };\n        const d = await r.json();\n        if (d?.succeeded === false || !d?.resp_data?.user) {\n          return { kind: 'auth', detail: 'zsxq /v2/users/self returned succeeded=false — anonymous' };\n        }\n        const u = d.resp_data.user;\n        return { ok: true, user_id: String(u.user_id || u.id || ''), name: String(u.name || u.nickname || '') };\n      } catch (e) {\n        return { kind: 'exception', detail: String(e && e.message || e) };\n      }\n    })()\n  `);\n  if (probe?.kind === 'auth') throw new AuthRequiredError('zsxq.com', probe.detail);\n  if (probe?.kind === 'http') throw new CommandExecutionError(`HTTP ${probe.httpStatus} from zsxq /v2/users/self`);\n  if (probe?.kind === 'exception') throw new CommandExecutionError(`zsxq whoami failed: ${probe.detail}`);\n  if (!probe?.ok) throw new CommandExecutionError(`Unexpected zsxq probe: ${JSON.stringify(probe)}`);\n  if (!probe.user_id) {\n    throw new AuthRequiredError('zsxq.com', 'zsxq /v2/users/self 200 but user_id missing — incomplete session');\n  }\n  return { user_id: probe.user_id, name: probe.name };\n}\n\nregisterSiteAuthCommands({\n  site: 'zsxq',\n  domain: 'zsxq.com',\n  loginUrl: 'https://wx.zsxq.com/login',\n  columns: ['user_id', 'name'],\n  verify: verifyZsxqIdentity,\n  // No-navigation poll: probe the API from the current page so the login-page\n  // QR code isn't reset by a goto on every interval.\n  poll: async (page) => {\n    const loggedIn = await page.evaluate(`(async () => {\n      try {\n        const r = await fetch('https://api.zsxq.com/v2/users/self', { credentials: 'include', headers: { Accept: 'application/json' } });\n        if (!r.ok) return false;\n        const d = await r.json();","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/zsxq/auth.js#L23-L59","documentation":"The probe got HTTP 200 from /v2/users/self (so the session is authenticated enough to pass), but the response body contained no resolvable user_id (u.user_id and u.id both empty). verifyZsxqIdentity throws AuthRequiredError('zsxq.com', '... incomplete session') because a valid identity could not be established.","triggerScenarios":"zsxq /v2/users/self returns 200 but with an unexpected/partial body: e.g. resp_data missing, fields renamed by an API change, or a session valid for cookies but not for full account data (partially invalidated login).","commonSituations":"zsxq API schema change breaking the user_id/nickname extraction;半 logged-in states after password change; server returning an empty resp_data for restricted accounts.","solutions":["Log out and log back in to zsxq in the browser profile to rebuild a complete session, then retry.","Log the raw probe response body to check whether the field names changed (user_id/id, name/nickname) and update the extraction if the API schema shifted.","Confirm the account is fully active (not suspended/restricted) by visiting zsxq.com in the same profile.","Update the library in case a newer version already handles the changed response schema."],"exampleFix":"// before\nconst u = await r.json();\nconst userId = String(u.user_id || u.id || '');\n// after\nconst d = await r.json();\nconst u = d?.resp_data ?? d;\nconst userId = String(u?.user_id || u?.id || '');\nif (!userId) throw new AuthRequiredError('zsxq.com', 'no user identity in response');","handlingStrategy":"validation","validationCode":"const r = await page.request.get('https://api.zsxq.com/v2/users/self');\nconst d = await r.json();\nconst u = d?.resp_data ?? d;\nif (!String(u?.user_id || u?.id || '')) throw new Error('session has no user_id — re-login required');","typeGuard":"const hasUserId = (d) => Boolean(String(d?.resp_data?.user_id || d?.resp_data?.id || d?.user_id || d?.id || ''));","tryCatchPattern":"try {\n  return await verifyZsxqIdentity(page);\n} catch (e) {\n  if ((e.message || '').includes('incomplete session')) {\n    await logoutAndLoginZsxq(page);\n    return verifyZsxqIdentity(page);\n  }\n  throw e;\n}","preventionTips":["Log out and back in after password changes or account security events.","Log the raw /v2/users/self body when this occurs to detect API schema drift.","Update the library when zsxq changes response field names."],"tags":["auth","schema-change","session"],"backgroundTag":"incomplete-session-identity","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}