can1357/oh-my-pi · error · SearchProviderError

Mojeek search failed: ${message}

Error message

Mojeek search failed: ${message}

What it means

Generic failure wrapper in callMojeekHtml: any non-SearchProviderError thrown during the Mojeek fetch (DNS failure, connection refused, TLS error, HTML parse error) is re-thrown as a SearchProviderError with the underlying message appended and status 503 (service unavailable semantics).

Source

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

			signal,
			timeoutMs: params.timeoutMs,
			randomizeHeaders: false,
			referer: MOJEEK_HOME_URL,
			browser: {
				homeUrl: MOJEEK_HOME_URL,
				afterNavigation: solveCaptcha,
				shouldFallback: isRobotPage,
				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);
	}

View on GitHub (pinned to 9690622007)

Solutions

  1. Read the embedded underlying message (after 'Mojeek search failed:') for the root cause
  2. Verify network egress to https://www.mojeek.com (DNS, proxy, firewall)
  3. Retry with backoff — 503 signals a transient condition
  4. Update Mojeek HTML parsing if the underlying message indicates a parse failure after a markup change

Example fix

// before
const results = await searchMojeek({ query });
// after
try {
  const results = await searchMojeek({ query });
} catch (err) {
  if (err instanceof SearchProviderError) {
    console.error(`Mojeek root cause: ${err.message}`);
    // fall back to another provider
  }
}
Defensive patterns

Strategy: fallback

Validate before calling

// basic connectivity preflight
const reachable = await fetch('https://www.mojeek.com', { method: 'HEAD' }).then(r => r.ok).catch(() => false);
if (!reachable) skipMojeek();

Try / catch

try {
  const res = await searchMojeek({ query });
} catch (err) {
  if (err instanceof SearchProviderError && err.message.startsWith('Mojeek search failed:')) {
    return fallbackProvider(query); // DNS/TLS/network-level failure
  }
  throw err;
}

Prevention

When it happens

Trigger: The fetch/parse pipeline throws a plain Error — DNS resolution failure, connection reset, certificate error, or an internal parse crash — and the signal is not aborted.

Common situations: Offline machine or blocked egress to mojeek.com; corporate proxy intercepting TLS; Mojeek temporarily unreachable; bugs in HTML parsing after Mojeek markup changes.

Related errors


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