jackwener/OpenCLI · error · CommandExecutionError

${errorPrefix ? `${errorPrefix}: ` : ''}Boss API returned ma

Error message

${errorPrefix ? `${errorPrefix}: ` : ''}Boss API returned malformed response

What it means

assertOk first checks that the response is a non-null object; if not, it throws CommandExecutionError 'Boss API returned malformed response' (optionally prefixed). This happens when the wapi endpoint returns nothing parseable into an object — e.g. an HTML error/anti-bot page that still parsed oddly, or a null/undefined result from page.evaluate after the XHR resolved with non-JSON that later got coerced. It is a defensive shape check before reading data.code.

Source

Thrown at clis/boss/utils.js:88

 * Recruiter-only commands (recommend, joblist, stats, resume, mark,
 * exchange, invite, greet, batchgreet) have no geek-side equivalent;
 * surfacing this as a generic COMMAND_EXEC hides what the user must do.
 * chatlist / chatmsg avoid this path by using `allowNonZero: true` and
 * branching to the geek-side fetch when they see code 24.
 */
function checkRecruiterSide(data) {
    if (data.code === IDENTITY_MISMATCH_CODE) {
        throw new AuthRequiredError(BOSS_DOMAIN, RECRUITER_ONLY_MSG);
    }
}
/**
 * Throw if the API response is not code 0.
 * Checks for cookie expiry first, then identity mismatch, then throws
 * with the provided message.
 */
export function assertOk(data, errorPrefix) {
    if (!data || typeof data !== 'object') {
        throw new CommandExecutionError(`${errorPrefix ? `${errorPrefix}: ` : ''}Boss API returned malformed response`);
    }
    if (data.code === 0)
        return;
    checkEnvironment(data);
    checkAuth(data);
    checkRecruiterSide(data);
    const prefix = errorPrefix ? `${errorPrefix}: ` : '';
    throw new CommandExecutionError(`${prefix}${data.message || 'Unknown error'} (code=${data.code})`);
}
/**
 * Make a credentialed XHR request via page.evaluate().
 *
 * This is the single XHR template — no more copy-pasting the same 15-line
 * XMLHttpRequest boilerplate across every adapter.
 *
 * @returns Parsed JSON response
 * @throws On network error, timeout, JSON parse failure, or cookie expiry
 */

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Log the raw response (set OPENCLI_VERBOSE or capture xhr.responseText) to see what BOSS actually returned.
  2. Re-login in the automation Chrome to clear any interstitial/captcha page, then retry.
  3. Retry after a delay — transient gateway/CDN errors often resolve.
  4. Check for VPN/proxy interference that could inject HTML error pages.
  5. Use fetchFriendList-style callers with allowNonZero:false removed only if you intend to handle raw shapes yourself; otherwise upgrade the library version with improved parse handling.

Example fix

// before
const data = await bossFetch(page, url); // data may be null
const list = data.zpData.friendList;
// after
const data = await bossFetch(page, url);
if (!data || typeof data !== 'object') throw new Error('unexpected non-object BOSS response');
const list = data.zpData?.friendList ?? [];
Defensive patterns

Strategy: type-guard

Validate before calling

function looksLikeBossEnvelope(v) {
  return !!v && typeof v === 'object' && typeof v.code === 'number';
}

Type guard

function isBossResponse(v) {
  return typeof v === 'object' && v !== null && !Array.isArray(v) && 'code' in v;
}

Try / catch

try {
  const data = await bossFetch(page, url);
  if (!isBossResponse(data)) throw new Error('non-object BOSS payload; session or CDN issue');
} catch (e) {
  if (/malformed response/.test(e.message)) {
    await reloginIfInterstitial(page); // clear captcha/login HTML pages
    return bossFetch(page, url);
  }
  throw e;
}

Prevention

When it happens

Trigger: bossFetch passes data to assertOk but data is null/undefined/non-object — e.g. BOSS returned an empty body, a redirect HTML page string, or page.evaluate serializing a non-object response; assertOk is also called directly by captureJobList on such payloads.

Common situations: BOSS serving an anti-bot or login HTML page instead of JSON; gateway timeouts returning empty bodies; a mobile-captcha interstitial; the session being rejected at CDN level so the 'JSON' is actually markup.

Understand the failure class

Related errors


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