can1357/oh-my-pi · error · SearchProviderError

Jina API response reported failure (${payload.code})

Error message

Jina API response reported failure (${payload.code})

What it means

Thrown by callJinaSearch when the Jina search API returns a JSON envelope whose numeric `code` field is not 200. Jina signals API-level failures (auth, quota, bad request) inside a 200-HTTP-response body via this code field, so the provider checks it explicitly and surfaces it as a SearchProviderError carrying the code as status.

Source

Thrown at packages/coding-agent/src/web/search/providers/jina.ts:86

	const response = await fetchImpl(requestUrl, {
		headers,
		signal: withHardTimeout(signal, timeoutMs),
	});

	if (!response.ok) {
		const errorText = await response.text();
		const classified = classifyProviderHttpError("jina", response.status, errorText);
		if (classified) throw classified;
		throw new SearchProviderError("jina", `Jina API error (${response.status}): ${errorText}`, response.status);
	}

	const payload = (await response.json()) as JinaSearchEnvelope | JinaSearchResponse | null;
	if (Array.isArray(payload)) return payload;
	if (!payload || typeof payload !== "object") {
		throw new SearchProviderError("jina", "Jina API returned invalid response: expected an object or array");
	}
	if (typeof payload.code === "number" && payload.code !== 200) {
		throw new SearchProviderError("jina", `Jina API response reported failure (${payload.code})`, payload.code);
	}
	if (!Array.isArray(payload.data)) {
		throw new SearchProviderError("jina", "Jina API returned invalid response: expected data array");
	}
	return payload.data as JinaSearchResponse;
}

/** Execute Jina web search. */
export async function searchJina(params: JinaSearchParams): Promise<SearchResponse> {
	const numResults = clampNumResults(params.num_results, DEFAULT_NUM_RESULTS, MAX_NUM_RESULTS);
	const keyOrResolver: ApiKey = params.authStorage.resolver("jina", {
		sessionId: params.sessionId,
	});
	const response = await withAuth(
		keyOrResolver,
		apiKey =>
			callJinaSearch(apiKey, params.query, numResults, params.site, params.signal, params.fetch, params.timeoutMs),
		{

View on GitHub (pinned to 9690622007)

Solutions

  1. Check payload.code in logs and map it: 401/403 -> fix JINA_API_KEY, 429 -> wait/backoff or upgrade plan
  2. Verify the JINA_API_KEY env var is set to a valid current key from Jina
  3. Retry with backoff for transient 5xx codes
  4. Handle the SearchProviderError by falling back to another search provider

Example fix

// before
const results = await searchJina({ query, apiKey });
// after
try {
  const results = await searchJina({ query, apiKey });
} catch (err) {
  if (err instanceof SearchProviderError && err.status === 401) {
    // refresh JINA_API_KEY before retrying
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling: ensure a key exists
if (!process.env.JINA_API_KEY) throw new Error('JINA_API_KEY not set');

Type guard

function isJinaEnvelope(p: unknown): p is { code: number; data?: unknown } {
  return typeof p === 'object' && p !== null && 'code' in p;
}

Try / catch

try {
  const res = await searchJina({ query, apiKey });
} catch (err) {
  if (err instanceof SearchProviderError) {
    if (err.status === 401 || err.status === 403) refreshJinaKey();
    else if (err.status === 429) scheduleRetry();
    else useFallbackProvider();
  } else throw err;
}

Prevention

When it happens

Trigger: Jina returns HTTP 200 but the body is a JinaSearchEnvelope with code != 200 (e.g. 401 unauthorized key, 429 quota exceeded, 400 malformed query). Any call to searchJina that reaches callJinaSearch with such an envelope.

Common situations: Expired or revoked JINA_API_KEY; exhausted free-tier credits; sending query parameters Jina rejects; Jina partial outages that return error envelopes instead of HTTP error statuses.

Related errors


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