jackwener/OpenCLI · error · CommandExecutionError

toutiao hot-board request failed: ${error?.message || error}

Error message

toutiao hot-board request failed: ${error?.message || error}

What it means

The toutiao `hot` command fetches Toutiao's public hot-board JSON (hot-event/hot-board) via fetch(). This error wraps any exception thrown by fetch itself — DNS failure, connection refused/reset, TLS error, timeout/abort, or invalid URL — re-thrown as a CommandExecutionError with the underlying cause message appended. It means the HTTP request never completed, so no response object exists.

Source

Thrown at clis/toutiao/hot.js:38

    strategy: Strategy.PUBLIC,
    browser: false,
    args: [
        { name: 'limit', type: 'int', default: 30, help: '返回条数 (1-50)' },
    ],
    columns: ['rank', 'group_id', 'title', 'query', 'hot_value', 'label', 'url', 'image_url'],
    func: async (kwargs) => {
        const limit = parseHotLimit(kwargs?.limit, 30);
        let resp;
        try {
            resp = await fetch(HOT_BOARD_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 hot-board request failed: ${error?.message || error}`);
        }
        if (!resp.ok) {
            throw new CommandExecutionError(`toutiao hot-board failed: HTTP ${resp.status}`);
        }
        let payload;
        try {
            payload = await resp.json();
        } catch (error) {
            throw new CommandExecutionError(`toutiao hot-board returned malformed JSON: ${error?.message || error}`);
        }
        if (payload?.status && payload.status !== 'success') {
            throw new CommandExecutionError(`toutiao hot-board returned status=${payload.status}`);
        }
        if (payload?.error || payload?.message) {
            throw new CommandExecutionError(`toutiao hot-board returned error: ${payload.error || payload.message}`);
        }
        const list = Array.isArray(payload?.data) ? payload.data : [];
        const rows = list.map(mapHotRow).filter(Boolean).slice(0, limit);

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Check basic network connectivity to the endpoint (curl -I with the same User-Agent/Referer headers) to confirm reachability.
  2. If 'fetch is not a function' appears, upgrade to Node 18+ or add a fetch polyfill (undici/node-fetch).
  3. Configure or disable proxy env vars (HTTPS_PROXY/NO_PROXY) so the request can route out; retry after transient failures.
  4. If toutiao.com is blocked from your region, run from a network that can reach it or supply an alternative mirror URL.

Example fix

// before
resp = await fetch(HOT_BOARD_URL, { headers });
// after — retry transient network failures
let resp;
for (let attempt = 0; attempt < 3; attempt++) {
  try {
    resp = await fetch(HOT_BOARD_URL, { headers });
    break;
  } catch (e) {
    if (attempt === 2) throw new CommandExecutionError(`toutiao hot-board request failed: ${e?.message || e}`);
    await new Promise(r => setTimeout(r, 500 * (attempt + 1)));
  }
}
Defensive patterns

Strategy: retry

Validate before calling

// verify the endpoint is reachable before invoking the command
curl -sS -o /dev/null -w '%{http_code}' \
  -H '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' \
  -H 'Referer: https://www.toutiao.com/' \
  https://www.toutiao.com/hot-event/hot-board/
// also: node -e "console.log(typeof fetch)" # must print 'function' (Node 18+)

Try / catch

try {
  const rows = await toutiaoHot({ limit: 30 });
} catch (e) {
  if (/request failed/.test(e.message)) {
    // network-layer failure: check connectivity/proxy, retry with backoff
    await sleep(1000);
    return toutiaoHot({ limit: 30 });
  }
  throw e;
}

Prevention

When it happens

Trigger: Calling the `toutiao hot` command when the network to www.toutiao.com is unreachable (offline, DNS failure, firewall/GFW blocking), the request is aborted (timeout/AbortSignal), or HOT_BOARD_URL becomes invalid — fetch() rejects before resp.ok can be checked.

Common situations: Running the CLI offline or behind a proxy that refuses connections; corporate/GFW blocking of toutiao.com; Node <18 where global fetch is undefined (TypeError 'fetch is not a function'); transient connection resets on mobile/VPN networks; typo'd or redirected HOT_BOARD_URL in utils.js.

Related errors


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