jackwener/OpenCLI · error · CommandExecutionError

Reuters search API returned an unreadable response

Error message

Reuters search API returned an unreadable response

What it means

The in-page fetch of the Reuters articles-by-search-v2 API returned a value that is null or not a plain object, so the CLI cannot inspect result.ok/status/body. The library throws this when page.evaluate resolves to something unexpected (undefined, a string, etc.), which usually means the page context was torn down, the evaluation was blocked, or the injected script failed silently before producing its structured result object. It is deliberately distinct from the auth-wall and non-OK cases so the caller knows the response shape itself is wrong.

Source

Thrown at clis/reuters/search.js:36

    args: [
        { name: 'query', required: true, positional: true, help: 'Search query' },
        { name: 'limit', type: 'int', default: 10, help: 'Number of results (1-40)' },
    ],
    columns: ['rank', 'title', 'date', 'section', 'section_path', 'authors', 'url'],
    func: async (page, kwargs) => {
        const limit = parseLimit(kwargs.limit);
        const query = String(kwargs.query || '').trim();
        if (!query) {
            throw new ArgumentError('Search query cannot be empty', 'Provide a non-empty keyword');
        }
        await page.goto('https://www.reuters.com');
        await page.wait(2);
        const result = await page.evaluate(buildSearchScript(query, limit));
        if (result?.error) {
            throw new CommandExecutionError(`Reuters search failed inside the page: ${result.error}`);
        }
        if (!result || typeof result !== 'object') {
            throw new CommandExecutionError('Reuters search API returned an unreadable response');
        }
        if (isAuthStatus(result.status) || looksAuthWallText(result.textPreview)) {
            throw new AuthRequiredError(
                'www.reuters.com',
                `Reuters search requires an accessible Reuters browser session or completed human verification${result.status ? ` (HTTP ${result.status})` : ''}`,
            );
        }
        if (result.ok !== true) {
            const status = Number.isFinite(result.status) && result.status > 0
                ? `HTTP ${result.status}${result.statusText ? ` ${result.statusText}` : ''}`
                : 'no upstream response';
            throw new CommandExecutionError(`Reuters search API failed (${status})`);
        }
        if (!result.body) {
            const detail = result.parseError ? `: ${result.parseError}` : '';
            throw new CommandExecutionError(
                `Reuters search returned a non-JSON body${detail}`,
                'Open www.reuters.com in your browser and clear any challenge before retrying.',

View on GitHub (pinned to 49907e53dc)

Solutions

  1. Retry the command — transient navigation/teardown of the reuters.com tab often resolves it on a second run.
  2. Run the auth/login flow so an authenticated, settled www.reuters.com tab exists before searching (Strategy.COOKIE requires it).
  3. Clear the Datadome challenge by opening www.reuters.com in the browser, then retry.
  4. Inspect/patch buildSearchScript in clis/reuters/utils.js to guarantee it always returns the structured result object even on internal failure (catch and return {ok:false,error:...}).

Example fix

// before
const result = await page.evaluate(buildSearchScript(query, limit));
// after (guard in the injected script)
const result = await page.evaluate(buildSearchScript(query, limit));
if (!result || typeof result !== 'object') {
    await page.goto('https://www.reuters.com');
    await page.wait(2);
    result = await page.evaluate(buildSearchScript(query, limit));
}
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure a settled, authenticated tab before evaluating
await page.goto('https://www.reuters.com');
await page.wait(2);
if (!page.url().includes('reuters.com')) throw new Error('tab redirected away; re-authenticate first');

Type guard

function isSearchResult(v) {
  return v !== null && typeof v === 'object' && !Array.isArray(v) && 'ok' in v;
}
const result = await page.evaluate(buildSearchScript(query, limit));
if (!isSearchResult(result)) { /* retry or surface unreadable-response error */ }

Try / catch

try {
  const rows = await runReutersSearch(query, limit);
} catch (e) {
  if (e.message.includes('unreadable response')) {
    await page.goto('https://www.reuters.com'); await page.wait(2);
    rows = await runReutersSearch(query, limit); // one retry
  } else throw e;
}

Prevention

When it happens

Trigger: page.evaluate(buildSearchScript(query, limit)) resolves to null/undefined/non-object — e.g. the injected IIFE threw before returning, the tab navigated away mid-evaluate, or the browser returned a serialized primitive.

Common situations: Reuters page redirected or reloaded while the search script ran; Datadome replaced the document so the script never returned its object; browser session timed out; a bug/version change in buildSearchScript stops it from returning a value.

Related errors


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