{"record":{"id":"944c399dbdcb846c","repo":"jackwener/OpenCLI","slug":"fetch-error-944c39","errorCode":"FETCH_ERROR","errorMessage":"Bloomberg served the bot-protection page instead of article content","messagePattern":"Bloomberg served the bot-protection page instead of article content","errorType":"error_code","errorClass":"CliError","httpStatus":null,"severity":"error","filePath":"clis/bloomberg/news.js","lineNumber":85,"sourceCode":"          summary: story.summary || story.socialDescription || story.seoDescription || document.querySelector('meta[name=\"description\"]')?.getAttribute('content') || '',\n          url: story.url || story.readingUrl || location.href,\n          body: story.body || null,\n          lede: story.lede || null,\n          ledeImageUrl: story.ledeImageUrl || null,\n          socialImageUrl: story.socialImageUrl || null,\n          imageAttachments: story.imageAttachments || {},\n          videoAttachments: story.videoAttachments || {},\n        }\n      };\n    })()`);\n        let result = await loadStory();\n        // Retry once — Bloomberg pages sometimes hydrate slowly.\n        if (result?.errorCode === 'NO_NEXT_DATA' || result?.errorCode === 'NO_STORY') {\n            await page.wait(4);\n            result = await loadStory();\n        }\n        if (result?.errorCode === 'ROBOT_PAGE') {\n            throw new CliError('FETCH_ERROR', 'Bloomberg served the bot-protection page instead of article content', 'Try again later or open the article in a regular Chrome session first, then rerun the command. This command uses your current Bloomberg access and does not bypass paywall or entitlement checks.');\n        }\n        if (result?.errorCode) {\n            throw new CliError('PARSE_ERROR', `Bloomberg page did not expose article story data (${result.errorCode})`, 'This command currently works on standard Bloomberg story/article pages that expose __NEXT_DATA__. Audio, video, newsletter, or other non-standard/blocked pages may not work. Access still depends on your current Bloomberg session.');\n        }\n        const story = result?.story;\n        if (!story) {\n            throw new CliError('PARSE_ERROR', 'Failed to extract Bloomberg story data', 'Bloomberg may have changed the page structure.');\n        }\n        const content = renderStoryBody(story.body);\n        if (!content) {\n            throw new CliError('PARSE_ERROR', 'Bloomberg article body was empty after parsing', 'Bloomberg may have changed the story-body format, the URL may not point to a standard article page, or the page may not be accessible in your current Bloomberg session.');\n        }\n        return [{\n                title: story.headline || '',\n                summary: story.summary || '',\n                link: story.url || url,\n                mediaLinks: extractStoryMediaLinks(story),\n                content,","sourceCodeStart":67,"sourceCodeEnd":103,"githubUrl":"https://github.com/jackwener/OpenCLI/blob/49907e53dc3ade5c223ff0c4c2c2785687cec4e6/clis/bloomberg/news.js#L67-L103","documentation":"Thrown when Bloomberg responds with its bot-protection (ROBOT_PAGE) interstitial instead of real article content. The library classifies it as FETCH_ERROR because the request never obtained the intended resource. It deliberately does not bypass the protection and relies on your existing browser session/access.","triggerScenarios":"Calling a Bloomberg news command whose headless page load detects automation — result.errorCode === 'ROBOT_PAGE' after the initial load and retry.","commonSituations":"Running from a datacenter IP or CI runner; cookies/session absent or expired; too many automated requests in a short window; missing Chrome profile the bot check expects.","solutions":["Open the article URL in a regular Chrome session to clear the bot check, then rerun the command","Wait and retry later — protections are often rate/time based","Run the command from a residential IP rather than a datacenter/CI environment","Reuse an authenticated browser profile/session that has passed the check"],"exampleFix":"// before\nconst story = await news.getArticle(url);\n// after\ntry {\n  const story = await news.getArticle(url);\n} catch (e) {\n  if (e.code === 'FETCH_ERROR' && /bot-protection/.test(e.message)) {\n    // alert operator to open a browser session; retry with backoff\n    await wait(60_000);\n  }\n}","handlingStrategy":"retry","validationCode":"null","typeGuard":"null","tryCatchPattern":"try {\n  const story = await news.getArticle(url);\n} catch (e) {\n  if (e.code === 'FETCH_ERROR' && /bot-protection/.test(e.message)) {\n    await sleep(60_000); // exponential backoff before retry\n  }\n}","preventionTips":["Use a browser session/profile that has cleared the bot check","Avoid datacenter IPs and high request rates","Retry with long backoff instead of hammering the endpoint"],"tags":["bot-detection","scraping","bloomberg"],"backgroundTag":"bot-protection-blocked","analyzedSha":"49907e53dc3ade5c223ff0c4c2c2785687cec4e6","analyzedAt":"2026-08-29T08:14:47.543Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}