can1357/oh-my-pi · error · SearchProviderError
Mojeek HTML error (${page.status})
Error message
Mojeek HTML error (${page.status}) What it means
Thrown by callMojeekHtml when Mojeek returns an HTTP status outside 2xx that is not a robot wall and not classified by classifyProviderHttpError. The status is attached to the SearchProviderError; the message is simply 'Mojeek HTML error (<status>)'.
Source
Thrown at packages/coding-agent/src/web/search/providers/mojeek.ts:181
const message = error instanceof Error ? error.message : String(error);
throw new SearchProviderError("mojeek", `Mojeek search failed: ${message}`, 503);
}
// Robot walls: the ALTCHA proof-of-work captcha page arrives as HTTP 200
// (`<title>Captcha</title>`, `altcha-widget`) and the "automated queries"
// refusal as HTTP 403. Both bodies are more actionable than their raw
// statuses, so check them before the generic status handling.
if (isRobotPage(page)) {
throw new SearchProviderError(
"mojeek",
"Mojeek blocked the request with its automated-queries wall. Mojeek rate-limits scripted searches from datacenter/shared-egress IPs; retry later or configure another provider such as Brave, Tavily, Exa, or Kagi.",
429,
);
}
if (page.status < 200 || page.status >= 300) {
const classified = classifyProviderHttpError("mojeek", page.status, page.html);
if (classified) throw classified;
throw new SearchProviderError("mojeek", `Mojeek HTML error (${page.status})`, page.status);
}
return page.html;
}
/** Execute a Mojeek web search against the standard HTML results page. */
export async function searchMojeek(params: SearchParams): Promise<SearchResponse> {
const numResults = clampNumResults(params.numSearchResults ?? params.limit, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
const html = await callMojeekHtml(params, numResults);
const parsed = parseHtmlResults(html);
const sources: SearchSource[] = [];
const seen = new Set<string>();
for (const result of parsed) {
if (seen.has(result.url)) continue;
seen.add(result.url);
sources.push({ title: result.title, url: result.url, snippet: result.snippet });
if (sources.length >= numResults) break;
}View on GitHub (pinned to 9690622007)
Solutions
- Check the attached status (err.status) — 5xx means retry with backoff
- Check Mojeek's service status for outages
- Reduce request rate if seeing repeated 4xx/5xx
- Update classifyProviderHttpError if this status should map to a richer classified error
Example fix
// before
const results = await searchMojeek({ query });
// after
try {
const results = await searchMojeek({ query });
} catch (err) {
if (err instanceof SearchProviderError && (err.status ?? 0) >= 500) {
// transient Mojeek outage — retry or use fallback provider
}
} Defensive patterns
Strategy: retry
Try / catch
try {
const res = await searchMojeek({ query });
} catch (err) {
if (err instanceof SearchProviderError && err.message.startsWith('Mojeek HTML error (5')) {
await Bun.sleep(2000); // transient server error: bounded retry
} else throw err;
} Prevention
- Retry only 5xx statuses with capped exponential backoff
- Check Mojeek status before concluding a persistent failure
- Fail over to another provider after N consecutive server errors
- Extend classifyProviderHttpError when new statuses appear repeatedly
When it happens
Trigger: Mojeek HTML endpoint returns e.g. 500/502/503 (server issues) or an unclassified 4xx, with page content that does not match isRobotPage.
Common situations: Mojeek server-side incidents; maintenance windows returning 5xx; unclassified 403/405 variants; intermittent upstream failures.
Related errors
- Share upload to ${base} failed: HTTP ${res.status}${detail ?
- Ecosia HTML error (${page.status})
- V2 remote compaction failed (${response.status} ${response.s
- No response body for V2 compaction streaming
- formatCompactionV2Failure(event, type)
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/8195243af2890dc0.
Report an issue: GitHub.