{"record":{"id":"56b1e7680a938b2e","repo":"jackwener/OpenCLI","slug":"errorprefix-errorprefix-boss-api-r","errorCode":null,"errorMessage":"${errorPrefix ? `${errorPrefix}: ` : ''}Boss API returned malformed response","messagePattern":"(.+?): ` : ''\\}Boss API returned malformed response","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/boss/utils.js","lineNumber":88,"sourceCode":" * Recruiter-only commands (recommend, joblist, stats, resume, mark,\n * exchange, invite, greet, batchgreet) have no geek-side equivalent;\n * surfacing this as a generic COMMAND_EXEC hides what the user must do.\n * chatlist / chatmsg avoid this path by using `allowNonZero: true` and\n * branching to the geek-side fetch when they see code 24.\n */\nfunction checkRecruiterSide(data) {\n    if (data.code === IDENTITY_MISMATCH_CODE) {\n        throw new AuthRequiredError(BOSS_DOMAIN, RECRUITER_ONLY_MSG);\n    }\n}\n/**\n * Throw if the API response is not code 0.\n * Checks for cookie expiry first, then identity mismatch, then throws\n * with the provided message.\n */\nexport function assertOk(data, errorPrefix) {\n    if (!data || typeof data !== 'object') {\n        throw new CommandExecutionError(`${errorPrefix ? `${errorPrefix}: ` : ''}Boss API returned malformed response`);\n    }\n    if (data.code === 0)\n        return;\n    checkEnvironment(data);\n    checkAuth(data);\n    checkRecruiterSide(data);\n    const prefix = errorPrefix ? `${errorPrefix}: ` : '';\n    throw new CommandExecutionError(`${prefix}${data.message || 'Unknown error'} (code=${data.code})`);\n}\n/**\n * Make a credentialed XHR request via page.evaluate().\n *\n * This is the single XHR template — no more copy-pasting the same 15-line\n * XMLHttpRequest boilerplate across every adapter.\n *\n * @returns Parsed JSON response\n * @throws On network error, timeout, JSON parse failure, or cookie expiry\n */","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/boss/utils.js#L70-L106","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Log the raw response (set OPENCLI_VERBOSE or capture xhr.responseText) to see what BOSS actually returned.","Re-login in the automation Chrome to clear any interstitial/captcha page, then retry.","Retry after a delay — transient gateway/CDN errors often resolve.","Check for VPN/proxy interference that could inject HTML error pages.","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."],"exampleFix":"// before\nconst data = await bossFetch(page, url); // data may be null\nconst list = data.zpData.friendList;\n// after\nconst data = await bossFetch(page, url);\nif (!data || typeof data !== 'object') throw new Error('unexpected non-object BOSS response');\nconst list = data.zpData?.friendList ?? [];","handlingStrategy":"type-guard","validationCode":"function looksLikeBossEnvelope(v) {\n  return !!v && typeof v === 'object' && typeof v.code === 'number';\n}","typeGuard":"function isBossResponse(v) {\n  return typeof v === 'object' && v !== null && !Array.isArray(v) && 'code' in v;\n}","tryCatchPattern":"try {\n  const data = await bossFetch(page, url);\n  if (!isBossResponse(data)) throw new Error('non-object BOSS payload; session or CDN issue');\n} catch (e) {\n  if (/malformed response/.test(e.message)) {\n    await reloginIfInterstitial(page); // clear captcha/login HTML pages\n    return bossFetch(page, url);\n  }\n  throw e;\n}","preventionTips":["Re-login when HTML interstitials start replacing JSON payloads","Retry transiently; empty/gateway responses are often momentary","Check VPN/proxy for injected HTML error pages","Run with OPENCLI_VERBOSE to capture raw response bodies for diagnosis"],"tags":["malformed-response","api-shape","network"],"backgroundTag":"malformed-api-response","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}