can1357/oh-my-pi · warning · SearchProviderError

Ecosia blocked the request with a Cloudflare bot challenge.

Error message

Ecosia blocked the request with a Cloudflare bot challenge. Ecosia's firewall throttles automated searches from datacenter/shared-egress IPs; try another web search provider such as DuckDuckGo, Brave, or Tavily.

What it means

Ecosia served a page containing a Cloudflare bot challenge instead of search results. The provider detects this (isBlockedPage) and raises HTTP 429. Ecosia's firewall throttles automated searches coming from datacenter or shared-egress IPs, so requests from servers/VPNs/CI runners are frequently challenged.

Source

Thrown at packages/coding-agent/src/web/search/providers/ecosia.ts:135

				homeUrl: ECOSIA_HOME_URL,
				ready: {
					selector: 'article[data-test-id="organic-result"]',
					timeoutMs: RESULT_RENDER_TIMEOUT_MS,
				},
				shouldFallback: isBlockedPage,
			},
		});
	} catch (error) {
		if (error instanceof SearchProviderError || params.signal?.aborted) throw error;
		if (signal.aborted) {
			throw new SearchProviderError("ecosia", "Ecosia search timed out.", 504);
		}
		const message = error instanceof Error ? error.message : String(error);
		throw new SearchProviderError("ecosia", `Ecosia search failed: ${message}`, 503);
	}

	if (isBlockedPage(page)) {
		throw new SearchProviderError(
			"ecosia",
			"Ecosia blocked the request with a Cloudflare bot challenge. Ecosia's firewall throttles automated searches from datacenter/shared-egress IPs; try another web search provider such as DuckDuckGo, Brave, or Tavily.",
			429,
		);
	}
	if (page.status < 200 || page.status >= 300) {
		const classified = classifyProviderHttpError("ecosia", page.status, page.html);
		if (classified) throw classified;
		throw new SearchProviderError("ecosia", `Ecosia HTML error (${page.status})`, page.status);
	}
	return page.html;
}

/** Execute an Ecosia web search and parse the server-rendered result page. */
export async function searchEcosia(params: SearchParams): Promise<SearchResponse> {
	const numResults = clampNumResults(params.numSearchResults ?? params.limit, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
	const html = await callEcosiaHtml(params);
	const parsed = parseHtmlResults(html);

View on GitHub (pinned to 9690622007)

Solutions

  1. Configure a different web search provider (DuckDuckGo, Brave, or Tavily) via an API key — these are the explicitly recommended alternatives.
  2. Run from a residential/different network IP if Ecosia must be used.
  3. Reduce Ecosia query rate to avoid triggering throttling.
  4. Check for an Ecosia API integration or paid search API instead of HTML scraping.

Example fix

// before
const results = await search({ provider: "ecosia", query });
// after
const results = await search({ provider: "brave", query }); // with BRAVE_API_KEY set
Defensive patterns

Strategy: fallback

Validate before calling

const res = await fetch("https://www.ecosia.org/search?q=test");
const html = await res.text();
const likelyBlocked = html.includes("cf-challenge") || html.includes("Just a moment...");
if (likelyBlocked) console.warn("Ecosia will challenge this IP; use another provider");

Try / catch

try {
  const res = await search({ provider: "ecosia", query });
} catch (e) {
  if (e instanceof SearchProviderError && e.status === 429) {
    const res = await search({ provider: "duckduckgo", query }); // recommended fallback
  } else throw e;
}

Prevention

When it happens

Trigger: callEcosiaHtml fetched a 200-class HTML page but isBlockedPage(page) matched Cloudflare challenge markers in the markup. Typical when running from cloud VMs, containers, VPNs, or after many rapid automated queries from one IP.

Common situations: Running the coding agent on an AWS/GCP/Azure box; corporate shared NAT egress; scraping Ecosia at high volume; Ecosia tightening bot protection after a markup change.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/b7bb4ab0f61f9ce7. Report an issue: GitHub.