{"record":{"id":"8551966d453dc510","repo":"jackwener/OpenCLI","slug":"securityid","errorCode":null,"errorMessage":"该聊天缺少 securityId，无法获取历史消息","messagePattern":"该聊天缺少 securityId，无法获取历史消息","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/boss/chatmsg.js","lineNumber":42,"sourceCode":"    };\n}\n\nfunction mapGeekMsg(m, friend) {\n    const fromUid = m.from && m.from.uid;\n    const isFromBoss = fromUid != null && String(fromUid) === String(friend.uid);\n    return {\n        from: isFromBoss ? '对方' : '我',\n        type: TYPE_MAP[m.type] || `其他(${m.type})`,\n        text: m.text || m.body?.text || m.body?.content || m.body?.showText ||\n            JSON.stringify(m.body || {}).slice(0, 120),\n        time: m.time ? new Date(m.time).toLocaleString('zh-CN') : '',\n    };\n}\n\nasync function bossChatMsg(page, kwargs, existingFriend) {\n    const friend = existingFriend ?? await findFriendByUid(page, kwargs.uid);\n    if (!friend) throw new EmptyResultError('boss chatmsg', '未找到该候选人');\n    if (!friend.securityId) throw new CommandExecutionError('该聊天缺少 securityId，无法获取历史消息');\n    const gid = friend.uid;\n    const securityId = encodeURIComponent(friend.securityId);\n    const msgUrl = `https://www.zhipin.com/wapi/zpchat/boss/historyMsg?gid=${gid}&securityId=${securityId}&page=${kwargs.page}&c=20&src=0`;\n    const msgData = await bossFetch(page, msgUrl);\n    const messages = msgData.zpData?.messages ?? msgData.zpData?.historyMsgList;\n    if (!Array.isArray(messages)) {\n        throw new CommandExecutionError('Boss recruiter history response did not include a message list');\n    }\n    if (messages.length === 0) {\n        throw new EmptyResultError('boss chatmsg', 'Boss returned no messages for this chat.');\n    }\n    return messages.map((m) => mapBossMsg(m, friend));\n}\n\nasync function geekChatMsg(page, kwargs, encryptSystemId) {\n    const friend = await findGeekFriendByUid(page, kwargs.uid, { encryptSystemId });\n    if (!friend) throw new EmptyResultError('boss chatmsg', '未找到该聊天（geek 侧）');\n    if (!friend.securityId) throw new CommandExecutionError('该聊天缺少 securityId，无法获取历史消息');","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/boss/chatmsg.js#L24-L60","documentation":"bossChatMsg throws CommandExecutionError('该聊天缺少 securityId，无法获取历史消息') when the matched chat entry exists but its securityId field is missing or empty. BOSS's historyMsg endpoint requires both gid and securityId as path parameters, so the library refuses to issue a request it knows will fail. This is a data-shape defect in the scraped friend record, not a user input problem.","triggerScenarios":"The chat list DOM/API response used by findFriendByUid returned a friend object without a securityId for this conversation — e.g. BOSS changed the field name/attribute in the friend list payload, or a system/placeholder chat entry has no securityId.","commonSituations":"BOSS front-end update renaming the securityId attribute so scraping yields undefined; very new or system-generated conversations lacking the field; partially rendered chat list read too early.","solutions":["Re-run the command; if the chat list was read mid-render, a fresh pass often populates securityId.","Refresh the chat list page and retry: `opencli boss chatmsg <uid> --side boss` after re-login/reload.","If it persists, the friend-list scraper in clis/boss/utils.js likely needs updating for a BOSS DOM/API change — check for a newer version of the CLI package.","Try the same uid with side=geek (auto mode) in case the geek-side record carries a securityId.","Report/inspect the raw friend entry (enable verbose output) to confirm securityId is truly absent before filing a bug."],"exampleFix":"// before\nconst friend = await findFriendByUid(page, kwargs.uid);\n// after — retry after forcing a fresh chat-list read\nawait navigateToChat(page);\nconst friend = await findFriendByUid(page, kwargs.uid);\nif (friend && !friend.securityId) throw new Error('stale chat list — reload and retry');","handlingStrategy":"retry","validationCode":"// nothing the caller can check pre-call (server-side field); validate result shape after chatlist\nconst friends = await chatlist({});\nif (friends.length && friends.every(f => !f.securityId)) console.warn('chatlist missing securityId for all entries — scraper/DOM issue');","typeGuard":"function hasSecurityId(f) { return !!f && typeof f.securityId === 'string' && f.securityId.length > 0; }","tryCatchPattern":"try {\n  const msgs = await chatmsg(uid, { side: 'boss' });\n} catch (e) {\n  if (isCommandExecutionError(e) && /securityId/.test(e.message)) {\n    // reload session / retry once, then try the other side\n    return chatmsg(uid, { side: 'geek' });\n  }\n  throw e;\n}","preventionTips":["Retry once after a short delay — mid-render chat lists often lack securityId.","Fall back to the opposite side (boss/geek) which may carry the field.","Keep the CLI package updated for BOSS DOM changes.","Report persistent cases with verbose dumps of the friend object."],"tags":["boss-zhipin","missing-field","securityid","command-execution-error"],"backgroundTag":"missing-security-id","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}