jackwener/OpenCLI · error · CommandExecutionError

toutiao recommend returned malformed JSON: ${error?.message

Error message

toutiao recommend returned malformed JSON: ${error?.message || error}

What it means

This CommandExecutionError is thrown when resp.json() fails while parsing the toutiao recommend response — the body was not valid JSON. The parse error message is embedded so developers can see whether it was an unexpected token, empty body, etc.

Source

Thrown at clis/toutiao/recommend.js:58

        try {
            resp = await fetch(url, {
                headers: {
                    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
                    Accept: 'application/json',
                    Referer: 'https://www.toutiao.com/',
                },
            });
        } catch (error) {
            throw new CommandExecutionError(`toutiao recommend request failed: ${error?.message || error}`);
        }
        if (!resp.ok) {
            throw new CommandExecutionError(`toutiao recommend failed: HTTP ${resp.status}`);
        }
        let payload;
        try {
            payload = await resp.json();
        } catch (error) {
            throw new CommandExecutionError(`toutiao recommend returned malformed JSON: ${error?.message || error}`);
        }
        if (payload?.message && payload.message !== 'success') {
            throw new CommandExecutionError(`toutiao recommend returned message=${payload.message}`);
        }
        if (!Array.isArray(payload?.data)) {
            throw new CommandExecutionError('toutiao recommend returned a non-array data field');
        }
        const rows = payload.data.map(mapRecommendRow).filter(Boolean).slice(0, limit);
        if (rows.length === 0) {
            throw new EmptyResultError('toutiao recommend', `频道 ${category} 返回空列表。`);
        }
        // Re-rank (1..N) after filter so ranks are dense even if upstream had ads.
        return rows.map((row, idx) => ({ ...row, rank: idx + 1 }));
    },
});

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Dump the raw response body (resp.text()) once to see what upstream actually returned — usually it is an HTML block page.
  2. If it is an anti-bot page, add realistic headers/cookies or route through a different IP.
  3. Retry — truncated/corrupt bodies are often transient.
  4. Check whether a proxy or VPN in the path is mangling the response.

Example fix

// before
await recommend({ category: 'tech' });
// after
try {
  await recommend({ category: 'tech' });
} catch (e) {
  if (String(e.message).includes('malformed JSON')) {
    console.error('Upstream returned non-JSON (likely anti-bot page).', e.message);
  } else throw e;
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
  return await recommend({ category });
} catch (e) {
  if (String(e.message).includes('malformed JSON')) {
    // upstream returned non-JSON (likely anti-bot page); retry or degrade
    return [];
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling 'toutiao recommend' when the upstream returns HTML (anti-bot challenge page), an empty body, truncated gzip content, or any non-JSON payload with a 2xx status.

Common situations: Upstream serving a CAPTCHA/verification HTML page with status 200; a transparent proxy intercepting and rewriting the response; CDN error pages; response body truncated by network issues.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


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