jackwener/OpenCLI · error · CommandExecutionError

Boss API returned malformed response

Error message

Boss API returned malformed response

What it means

After the XHR resolves, bossFetch verifies the parsed payload is a non-null object before reading data.code; a null, undefined, array-less primitive, or empty result throws CommandExecutionError 'Boss API returned malformed response' (no prefix, unlike the assertOk variant). This guards against BOSS endpoints replying 200 with an empty or non-JSON-coercible body.

Source

Thrown at clis/boss/utils.js:142

        };
        xhr.onerror = () => reject(new Error('Network Error'));
        xhr.ontimeout = () => reject(new Error('Timeout'));
        xhr.send(${body ? JSON.stringify(body) : 'null'});
      });
    }
  `;
    let data;
    try {
        data = await page.evaluate(script);
    } catch (error) {
        if (error instanceof AuthRequiredError || error instanceof CommandExecutionError) {
            throw error;
        }
        const message = error instanceof Error ? error.message : String(error);
        throw new CommandExecutionError(`Boss API request failed: ${message}`);
    }
    if (!data || typeof data !== 'object') {
        throw new CommandExecutionError('Boss API returned malformed response');
    }
    // Auto-check auth unless caller opts out
    if (!opts.allowNonZero && data.code !== 0) {
        assertOk(data);
    }
    return data;
}
// ── Convenience helpers ─────────────────────────────────────────────────────
/**
 * Fetch the boss friend (chat) list.
 */
export async function fetchFriendList(page, opts = {}) {
    const pageNum = opts.pageNum ?? 1;
    const jobId = opts.jobId ?? '0';
    const url = `https://${BOSS_DOMAIN}/wapi/zprelation/friend/getBossFriendListV2.json?page=${pageNum}&status=0&jobId=${jobId}`;
    const data = await bossFetch(page, url, { allowNonZero: opts.allowNonZero });
    if (opts.allowNonZero && data.code !== 0) return data;
    const list = data.zpData?.friendList;

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry after a short delay — empty 200 bodies are often transient server-side hiccups.
  2. Re-login in the driven Chrome to rule out an interstitial page replacing the JSON payload.
  3. Verify the endpoint URL/version is current (BOSS may have changed the wapi path); update the library if so.
  4. Capture the raw responseText (via OPENCLI_VERBOSE or a custom page.evaluate) to diagnose what was actually returned.
  5. Wrap the call in retry-with-backoff for robustness against transient blanks.

Example fix

// before
const data = await bossFetch(page, url);
// after
const data = await retry(() => bossFetch(page, url), { attempts: 3, delayMs: 1000 });
Defensive patterns

Strategy: retry

Validate before calling

function looksLikeBossEnvelope(v) {
  return !!v && typeof v === 'object' && typeof v.code === 'number';
}
if (!looksLikeBossEnvelope(await bossFetch(page, probeUrl, { allowNonZero: true }))) {
  console.error('BOSS returning empty payloads; wait or re-login.');
}

Type guard

function isNonEmptyObject(v) {
  return typeof v === 'object' && v !== null && Object.keys(v).length > 0;
}

Try / catch

try {
  return await bossFetch(page, url);
} catch (e) {
  if (e instanceof CommandExecutionError && /malformed response/.test(e.message)) {
    await sleep(3000);
    return bossFetch(page, url);
  }
  throw e;
}

Prevention

When it happens

Trigger: wapi endpoint returns an empty body or a body that JSON.parse yields null/a primitive (e.g. bare string/number), so page.evaluate resolves with a non-object and the shape check at utils.js:141 fails.

Common situations: BOSS serving blank responses during maintenance or under risk-control; CDN edge returning 200 with empty payload; calling a deprecated/renamed wapi endpoint that no longer returns JSON.

Understand the failure class

Related errors


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