can1357/oh-my-pi · warning · SearchProviderError

Mojeek blocked the request with its automated-queries wall.

Error message

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.

What it means

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.

Source

Thrown at packages/coding-agent/src/web/search/providers/mojeek.ts:172

				attempts: 2,
				retryDelayMs: 1_000,
			},
		});
	} catch (error) {
		if (error instanceof SearchProviderError || params.signal?.aborted) throw error;
		if (signal.aborted) {
			throw new SearchProviderError("mojeek", "Mojeek search timed out.", 504);
		}
		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);

View on GitHub (pinned to 9690622007)

Solutions

  1. Switch to a provider that permits API access (Brave, Tavily, Exa, Kagi) as the error message suggests
  2. Reduce query rate and add backoff between Mojeek requests
  3. Egress via a residential/different IP or proxy
  4. Use Mojeek's official API with a key instead of the HTML endpoint

Example fix

// before
const results = await searchMojeek({ query });
// after
try {
  return await searchMojeek({ query });
} catch (err) {
  if (err instanceof SearchProviderError && err.status === 429) {
    return await searchBrave({ query }); // datacenter IP blocked by Mojeek
  }
  throw err;
}
Defensive patterns

Strategy: fallback

Validate before calling

// detect datacenter egress tendencies: keep request rate low and honor backoff
await limiter.acquire('mojeek', { minIntervalMs: 2000 });

Try / catch

try {
  const res = await searchMojeek({ query });
} catch (err) {
  if (err instanceof SearchProviderError && err.status === 429) {
    return await searchBrave({ query }); // Mojeek wall: switch providers
  }
  throw err;
}

Prevention

When it happens

Trigger: searchMojeek against the HTML endpoint where the response HTML contains captcha markers (`<title>Captcha</title>`, `altcha-widget`) or the 403 automated-queries refusal body.

Common situations: 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.

Related errors


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