{"record":{"id":"fda8d7774dbaf538","repo":"can1357/oh-my-pi","slug":"mojeek-blocked-the-request-with-its-automated-quer","errorCode":null,"errorMessage":"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.","messagePattern":"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\\.","errorType":"exception","errorClass":"SearchProviderError","httpStatus":429,"severity":"warning","filePath":"packages/coding-agent/src/web/search/providers/mojeek.ts","lineNumber":172,"sourceCode":"\t\t\t\tattempts: 2,\n\t\t\t\tretryDelayMs: 1_000,\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(\"mojeek\", \"Mojeek search timed out.\", 504);\n\t\t}\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthrow new SearchProviderError(\"mojeek\", `Mojeek search failed: ${message}`, 503);\n\t}\n\n\t// Robot walls: the ALTCHA proof-of-work captcha page arrives as HTTP 200\n\t// (`<title>Captcha</title>`, `altcha-widget`) and the \"automated queries\"\n\t// refusal as HTTP 403. Both bodies are more actionable than their raw\n\t// statuses, so check them before the generic status handling.\n\tif (isRobotPage(page)) {\n\t\tthrow new SearchProviderError(\n\t\t\t\"mojeek\",\n\t\t\t\"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.\",\n\t\t\t429,\n\t\t);\n\t}\n\tif (page.status < 200 || page.status >= 300) {\n\t\tconst classified = classifyProviderHttpError(\"mojeek\", page.status, page.html);\n\t\tif (classified) throw classified;\n\t\tthrow new SearchProviderError(\"mojeek\", `Mojeek HTML error (${page.status})`, page.status);\n\t}\n\treturn page.html;\n}\n\n/** Execute a Mojeek web search against the standard HTML results page. */\nexport async function searchMojeek(params: SearchParams): Promise<SearchResponse> {\n\tconst numResults = clampNumResults(params.numSearchResults ?? params.limit, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);\n\tconst html = await callMojeekHtml(params, numResults);\n\tconst parsed = parseHtmlResults(html);","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/web/search/providers/mojeek.ts#L154-L190","documentation":"Thrown by callMojeekHtml when the fetched page matches Mojeek's robot-wall signatures (isRobotPage): either the ALTCHA proof-of-work captcha page (HTTP 200 with captcha markers) or the automated-queries refusal (HTTP 403). Mojeek blocks scripted searches from datacenter/shared egress IPs; the error carries status 429.","triggerScenarios":"searchMojeek against the HTML endpoint where the response HTML contains captcha markers (`<title>Captcha</title>`, `altcha-widget`) or the 403 automated-queries refusal body.","commonSituations":"Running from a cloud/datacenter/VPN IP that Mojeek rate-limits; high query volume from one IP; scraping without an API key; shared CI egress IPs flagged by Mojeek.","solutions":["Switch to a provider that permits API access (Brave, Tavily, Exa, Kagi) as the error message suggests","Reduce query rate and add backoff between Mojeek requests","Egress via a residential/different IP or proxy","Use Mojeek's official API with a key instead of the HTML endpoint"],"exampleFix":"// before\nconst results = await searchMojeek({ query });\n// after\ntry {\n  return await searchMojeek({ query });\n} catch (err) {\n  if (err instanceof SearchProviderError && err.status === 429) {\n    return await searchBrave({ query }); // datacenter IP blocked by Mojeek\n  }\n  throw err;\n}","handlingStrategy":"fallback","validationCode":"// detect datacenter egress tendencies: keep request rate low and honor backoff\nawait limiter.acquire('mojeek', { minIntervalMs: 2000 });","typeGuard":null,"tryCatchPattern":"try {\n  const res = await searchMojeek({ query });\n} catch (err) {\n  if (err instanceof SearchProviderError && err.status === 429) {\n    return await searchBrave({ query }); // Mojeek wall: switch providers\n  }\n  throw err;\n}","preventionTips":["Throttle Mojeek queries (seconds between requests, not milliseconds)","Prefer Mojeek's official API with a key over HTML scraping","Avoid datacenter/VPN IPs known to be rate-limited","Configure Brave/Tavily/Exa/Kagi as fallback providers in advance"],"tags":["rate-limit","bot-detection","mojeek","blocked"],"backgroundTag":"http-429-rate-limited","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}