jackwener/OpenCLI · error · CommandExecutionError

toutiao recommend returned malformed JSON

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.

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.

Appendix: 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)