can1357/oh-my-pi · error · SearchProviderError
DuckDuckGo HTML error (${page.status})
Error message
DuckDuckGo HTML error (${page.status}) What it means
callDuckDuckGoHtml POSTs the query to DuckDuckGo's html.duckduckgo.com frontend via browserFetch. Any non-2xx status that classifyProviderHttpError cannot map to a more specific error becomes a SearchProviderError carrying the raw HTTP status, so callers can react to rate limits, outages, or gateway errors from the HTML endpoint.
Source
Thrown at packages/coding-agent/src/web/search/providers/duckduckgo.ts:321
async function callDuckDuckGoHtml(params: SearchParams, form: URLSearchParams, signal: AbortSignal): Promise<string> {
const page = await browserFetch(DUCKDUCKGO_HTML_URL, {
fetch: params.fetch ?? fetch,
signal,
timeoutMs: params.timeoutMs,
referer: "https://html.duckduckgo.com/",
init: {
method: "POST",
body: form.toString(),
},
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
const body = page.html;
if (page.status < 200 || page.status >= 300) {
const classified = classifyProviderHttpError("duckduckgo", page.status, body);
if (classified) throw classified;
throw new SearchProviderError("duckduckgo", `DuckDuckGo HTML error (${page.status})`, page.status);
}
if (isAnomalyResponse(body)) {
throw new SearchProviderError(
"duckduckgo",
"DuckDuckGo blocked the request with a bot-detection challenge. DuckDuckGo throttles automated HTML searches from datacenter/shared-egress IPs; configure a credentialed provider such as Brave, Tavily, Exa, or Kagi for reliable web search.",
429,
);
}
return body;
}
/** Execute a DuckDuckGo web search via the no-JS HTML frontend. */
export async function searchDuckDuckGo(params: SearchParams): Promise<SearchResponse> {
const numResults = clampNumResults(params.numSearchResults ?? params.limit, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
const signal = withHardTimeout(params.signal, params.timeoutMs);
const sources: SearchSource[] = [];View on GitHub (pinned to 9690622007)
Solutions
- Retry with backoff — many 5xx statuses are transient.
- Check the numeric status in the message: 429-style throttling means switch egress IP or use a credentialed provider.
- Fall back to another provider (Brave, Tavily, Exa, Kagi) via the provider chain.
- Verify network/proxy is not intercepting html.duckduckgo.com with an error page.
Example fix
// before
const res = await searchDuckDuckGo(params); // hard failure on 503
// after
try {
return await searchDuckDuckGo(params);
} catch (e) {
if (e instanceof SearchProviderError && (e.status ?? 0) >= 500) return retryWithBackoff(() => searchDuckDuckGo(params));
throw e;
} Defensive patterns
Strategy: retry
Type guard
function isDuckDuckGoHttpError(e: unknown): e is SearchProviderError {
return e instanceof SearchProviderError && e.provider === "duckduckgo" && /^DuckDuckGo HTML error \(\d+\)$/.test(e.message);
} Try / catch
try {
return await searchDuckDuckGo(params);
} catch (e) {
if (isDuckDuckGoHttpError(e)) {
const status = e.status ?? 0;
if (status >= 500 || status === 408) return retryWithBackoff(() => searchDuckDuckGo(params), 3);
if (status === 429) return fallbackProvider(params);
}
throw e;
} Prevention
- Prefer API-backed providers for production; treat DDG HTML scraping as best-effort.
- Add exponential backoff for 5xx statuses and immediate fallback for 429s.
- Monitor provider status codes to detect DDG endpoint changes early.
When it happens
Trigger: browserFetch returns a page with status outside 200–299 and classifyProviderHttpError("duckduckgo", status, body) returns undefined — e.g. unusual statuses (408, 500-series) or bodies not matching known block patterns.
Common situations: DuckDuckGo temporary outages or 5xx errors; datacenter IP rate limiting with a status not matching the anomaly page; corporate proxies returning gateway errors; DDG changing response codes for automated traffic.
Understand the failure class
Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.
Related errors
- DuckDuckGo blocked the request with a bot-detection challeng
- Ecosia search timed out.
- Auth broker returned no snapshot
- Auth broker returned no initial snapshot
- GitHub API rate limit exceeded while fetching release metada
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/5c2a030f600cf0a2.
Report an issue: GitHub.