jackwener/OpenCLI · error · CommandExecutionError

Boss geek chat list response did not include zpData.friendLi

Error message

Boss geek chat list response did not include zpData.friendList

What it means

fetchGeekFriendLabelList queries geekFilterByLabel and requires data.zpData.friendList to be an array of geek chat contacts. If the endpoint returns anything else (auth failure, error envelope, schema change) the library throws this CommandExecutionError rather than returning undefined. With opts.allowNonZero a non-zero code is returned instead of throwing, but a zero-code response still must contain friendList.

Source

Thrown at clis/boss/utils.js:420

      } catch (_) {}
      return '';
    })()
  `);
    return result || '';
}
/**
 * Fetch the job-seeker chat list (brief info, no securityId).
 * Use fetchGeekFriendInfoList to enrich with securityId before calling chatmsg.
 */
export async function fetchGeekFriendLabelList(page, opts = {}) {
    const labelId = opts.labelId ?? 0;
    const encryptSystemId = opts.encryptSystemId ?? '';
    const url = `https://${BOSS_DOMAIN}/wapi/zprelation/friend/geekFilterByLabel?labelId=${labelId}&encryptSystemId=${encodeURIComponent(encryptSystemId)}`;
    const data = await bossFetch(page, url, { allowNonZero: opts.allowNonZero });
    if (opts.allowNonZero && data.code !== 0) return data;
    const list = data.zpData?.friendList;
    if (!Array.isArray(list)) {
        throw new CommandExecutionError('Boss geek chat list response did not include zpData.friendList');
    }
    return list;
}
/**
 * Enrich a batch of geek friends with full fields including securityId.
 * Processes in batches of 50 to avoid oversized request bodies.
 */
export async function fetchGeekFriendInfoList(page, friendIds = []) {
    if (!friendIds.length) return [];
    const BATCH_SIZE = 50;
    const results = [];
    for (let i = 0; i < friendIds.length; i += BATCH_SIZE) {
        const batch = friendIds.slice(i, i + BATCH_SIZE).map(String);
        const body = `friendIds=${batch.join(',')}`;
        const data = await bossFetch(page, `https://${BOSS_DOMAIN}/wapi/zprelation/friend/getGeekFriendList.json`, {
            method: 'POST',
            body,
        });

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Re-login to Boss直聘 and retry labelList
  2. Verify the labelId passed to fetchGeekFriendLabelList is valid for this account
  3. Log the raw response body to distinguish auth/challenge responses from schema changes
  4. Retry later if the response shows rate-limiting/anti-bot interception

Example fix

// before
const list = data.zpData?.friendList;
if (!Array.isArray(list)) throw new CommandExecutionError('...');
// after
if (data.code !== 0) throw new AuthRequiredError(BOSS_DOMAIN, `geekFilterByLabel code=${data.code}`);
const list = Array.isArray(data.zpData?.friendList) ? data.zpData.friendList : [];
Defensive patterns

Strategy: type-guard

Validate before calling

const cookies = await page.getCookies({ url: `https://${BOSS_DOMAIN}` });
if (!cookies.some(c => /^wt2$|_uid/i.test(c.name) && c.value)) throw new Error('Boss session missing');

Type guard

function hasFriendList(d) { return Array.isArray(d?.zpData?.friendList); }

Try / catch

try { const list = await fetchGeekFriendLabelList(page, { labelId }); }
catch (e) { if (e instanceof CommandExecutionError) { await relogin(page); return fetchGeekFriendLabelList(page, { labelId }); } throw e; }

Prevention

When it happens

Trigger: Calling labelList when geekFilterByLabel returns code 0 but no zpData.friendList — session expired mid-run, label id invalid, anti-bot interception, or Boss renamed the field.

Common situations: Cookie expiry between commands; passing an invalid/removed labelId; WAPI returning an HTML challenge page parsed as JSON without friendList; upstream API schema change.

Related errors


AI-assisted analysis of jackwener/OpenCLI@49907e53dc (2026-08-29). Data as JSON: /api/errors/14b7bd9335e1d4ee. Report an issue: GitHub.