{"record":{"id":"290ad1aea1f5e5bb","repo":"can1357/oh-my-pi","slug":"ecosia-search-timed-out","errorCode":null,"errorMessage":"Ecosia search timed out.","messagePattern":"Ecosia search timed out\\.","errorType":"exception","errorClass":"SearchProviderError","httpStatus":504,"severity":"error","filePath":"packages/coding-agent/src/web/search/providers/ecosia.ts","lineNumber":128,"sourceCode":"\ttry {\n\t\tpage = await browserFetch(url.href, {\n\t\t\tfetch: params.fetch,\n\t\t\tsignal,\n\t\t\ttimeoutMs: params.timeoutMs,\n\t\t\treferer: ECOSIA_HOME_URL,\n\t\t\tbrowser: {\n\t\t\t\thomeUrl: ECOSIA_HOME_URL,\n\t\t\t\tready: {\n\t\t\t\t\tselector: 'article[data-test-id=\"organic-result\"]',\n\t\t\t\t\ttimeoutMs: RESULT_RENDER_TIMEOUT_MS,\n\t\t\t\t},\n\t\t\t\tshouldFallback: isBlockedPage,\n\t\t\t},\n\t\t});\n\t} catch (error) {\n\t\tif (error instanceof SearchProviderError || params.signal?.aborted) throw error;\n\t\tif (signal.aborted) {\n\t\t\tthrow new SearchProviderError(\"ecosia\", \"Ecosia search timed out.\", 504);\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new SearchProviderError(\"ecosia\", `Ecosia search failed: ${message}`, 503);\n\t}\n\n\tif (isBlockedPage(page)) {\n\t\tthrow new SearchProviderError(\n\t\t\t\"ecosia\",\n\t\t\t\"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.\",\n\t\t\t429,\n\t\t);\n\t}\n\tif (page.status < 200 || page.status >= 300) {\n\t\tconst classified = classifyProviderHttpError(\"ecosia\", page.status, page.html);\n\t\tif (classified) throw classified;\n\t\tthrow new SearchProviderError(\"ecosia\", `Ecosia HTML error (${page.status})`, page.status);\n\t}\n\treturn page.html;","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/search/providers/ecosia.ts#L110-L146","documentation":"callEcosiaHtml wraps browserFetch of Ecosia's search page in a hard-timeout signal. If the underlying fetch or render-wait throws and neither the outer params.signal nor a prior SearchProviderError explains it, a signal.aborted check distinguishes timeouts: it throws a SearchProviderError with status 504 when the composed hard-timeout signal fired.","triggerScenarios":"browserFetch (including the result-render wait for `article[data-test-id=\"organic-result\"]`) exceeds params.timeoutMs, aborting the withHardTimeout signal; the original error is then replaced by this 504 timeout error.","commonSituations":"Slow Ecosia page renders (Cloudflare interstitials delaying content); too-small timeoutMs passed in SearchParams; network latency to Ecosia; JS-render wait never matching the result selector after a page layout change.","solutions":["Increase timeoutMs in the search params to accommodate Ecosia's JS render time.","Retry — transient slowness often resolves; the provider chain may also fall back.","Check network connectivity/latency to ecosia.com from the running environment.","Switch to a faster or API-backed provider (Brave, Tavily) for latency-sensitive workloads."],"exampleFix":"// before\nawait search({ provider: \"ecosia\", query, timeoutMs: 5000 }); // render wait exceeds 5s\n// after\nawait search({ provider: \"ecosia\", query, timeoutMs: 20000 });","handlingStrategy":"retry","validationCode":"// pre-call sanity: ensure the timeout budget is realistic\nif (params.timeoutMs < 10000) console.warn(\"Ecosia HTML render typically needs >10s; low timeoutMs risks 504\");","typeGuard":"function isEcosiaTimeout(e: unknown): e is SearchProviderError {\n  return e instanceof SearchProviderError && e.provider === \"ecosia\" && e.status === 504 && e.message === \"Ecosia search timed out.\";\n}","tryCatchPattern":"try {\n  return await searchEcosia(params);\n} catch (e) {\n  if (isEcosiaTimeout(e)) return retryWithBackoff(() => searchEcosia({ ...params, timeoutMs: 20000 }));\n  if (e instanceof SearchProviderError && e.status === 429) return fallbackProvider(params);\n  throw e;\n}","preventionTips":["Set timeoutMs generously (>=15s) for JS-rendered scrapers like Ecosia.","Respect params.signal: caller aborts surface as-is, only internal hard timeouts become 504.","Prefer API providers for latency-sensitive paths and keep scrapers as fallback."],"tags":["timeout","network","ecosia","search-provider","http-status"],"backgroundTag":"request-timeout","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}