{"record":{"id":"4d3adff6bfcd334b","repo":"jackwener/OpenCLI","slug":"reuters-search-api-returned-an-unreadable-response","errorCode":null,"errorMessage":"Reuters search API returned an unreadable response","messagePattern":"Reuters search API returned an unreadable response","errorType":"exception","errorClass":"CommandExecutionError","httpStatus":null,"severity":"error","filePath":"clis/reuters/search.js","lineNumber":36,"sourceCode":"    args: [\n        { name: 'query', required: true, positional: true, help: 'Search query' },\n        { name: 'limit', type: 'int', default: 10, help: 'Number of results (1-40)' },\n    ],\n    columns: ['rank', 'title', 'date', 'section', 'section_path', 'authors', 'url'],\n    func: async (page, kwargs) => {\n        const limit = parseLimit(kwargs.limit);\n        const query = String(kwargs.query || '').trim();\n        if (!query) {\n            throw new ArgumentError('Search query cannot be empty', 'Provide a non-empty keyword');\n        }\n        await page.goto('https://www.reuters.com');\n        await page.wait(2);\n        const result = await page.evaluate(buildSearchScript(query, limit));\n        if (result?.error) {\n            throw new CommandExecutionError(`Reuters search failed inside the page: ${result.error}`);\n        }\n        if (!result || typeof result !== 'object') {\n            throw new CommandExecutionError('Reuters search API returned an unreadable response');\n        }\n        if (isAuthStatus(result.status) || looksAuthWallText(result.textPreview)) {\n            throw new AuthRequiredError(\n                'www.reuters.com',\n                `Reuters search requires an accessible Reuters browser session or completed human verification${result.status ? ` (HTTP ${result.status})` : ''}`,\n            );\n        }\n        if (result.ok !== true) {\n            const status = Number.isFinite(result.status) && result.status > 0\n                ? `HTTP ${result.status}${result.statusText ? ` ${result.statusText}` : ''}`\n                : 'no upstream response';\n            throw new CommandExecutionError(`Reuters search API failed (${status})`);\n        }\n        if (!result.body) {\n            const detail = result.parseError ? `: ${result.parseError}` : '';\n            throw new CommandExecutionError(\n                `Reuters search returned a non-JSON body${detail}`,\n                'Open www.reuters.com in your browser and clear any challenge before retrying.',","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/reuters/search.js#L18-L54","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the command — transient navigation/teardown of the reuters.com tab often resolves it on a second run.","Run the auth/login flow so an authenticated, settled www.reuters.com tab exists before searching (Strategy.COOKIE requires it).","Clear the Datadome challenge by opening www.reuters.com in the browser, then retry.","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:...})."],"exampleFix":"// before\nconst result = await page.evaluate(buildSearchScript(query, limit));\n// after (guard in the injected script)\nconst result = await page.evaluate(buildSearchScript(query, limit));\nif (!result || typeof result !== 'object') {\n    await page.goto('https://www.reuters.com');\n    await page.wait(2);\n    result = await page.evaluate(buildSearchScript(query, limit));\n}","handlingStrategy":"type-guard","validationCode":"// ensure a settled, authenticated tab before evaluating\nawait page.goto('https://www.reuters.com');\nawait page.wait(2);\nif (!page.url().includes('reuters.com')) throw new Error('tab redirected away; re-authenticate first');","typeGuard":"function isSearchResult(v) {\n  return v !== null && typeof v === 'object' && !Array.isArray(v) && 'ok' in v;\n}\nconst result = await page.evaluate(buildSearchScript(query, limit));\nif (!isSearchResult(result)) { /* retry or surface unreadable-response error */ }","tryCatchPattern":"try {\n  const rows = await runReutersSearch(query, limit);\n} catch (e) {\n  if (e.message.includes('unreadable response')) {\n    await page.goto('https://www.reuters.com'); await page.wait(2);\n    rows = await runReutersSearch(query, limit); // one retry\n  } else throw e;\n}","preventionTips":["Always land on and settle www.reuters.com before evaluating in-page scripts.","Make the injected script return {ok:false,error:...} on internal failure instead of nothing.","Wrap page.evaluate in a retry for transient tab teardown.","Log page.url() and a text snapshot when the result shape is wrong to diagnose redirection."],"tags":["browser-automation","network","serialization"],"backgroundTag":"unreadable-response-shape","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}