can1357/oh-my-pi · error · SearchProviderError

${err.message}

Error message

${err.message}

What it means

Re-thrown by searchKagi as a SearchProviderError wrapping a KagiApiError whose statusCode is not classified by classifyProviderHttpError (i.e. an unexceptional or unusual HTTP status). The original Kagi error message is preserved verbatim.

Source

Thrown at packages/coding-agent/src/web/search/providers/kagi.ts:68

				fetch: params.fetch,
			},
			params.authStorage,
		);

		return {
			provider: "kagi",
			sources: toSearchSources(result.sources, numResults),
			relatedQuestions: result.relatedQuestions.length > 0 ? result.relatedQuestions : undefined,
			requestId: result.requestId,
			answer: result.answer,
		};
	} catch (err) {
		if (err instanceof KagiApiError) {
			if (typeof err.statusCode === "number") {
				const classified = classifyProviderHttpError("kagi", err.statusCode, err.message);
				if (classified) throw classified;
			}
			throw new SearchProviderError("kagi", err.message, err.statusCode);
		}
		throw err;
	}
}

/** Search provider for Kagi web search. */
export class KagiProvider extends SearchProvider {
	readonly id = "kagi";
	readonly label = "Kagi";

	isAvailable(authStorage: AuthStorage): boolean {
		return authStorage.hasAuth("kagi");
	}

	search(params: SearchParamsWithFetch): Promise<SearchResponse> {
		const fetchImpl = params.fetch;

		return searchKagi({

View on GitHub (pinned to 9690622007)

Solutions

  1. Read err.status (the wrapped statusCode) and err.message for the actual Kagi failure reason
  2. Check your Kagi API key balance/subscription if the message indicates payment or quota
  3. Retry with backoff for 5xx-class statuses
  4. Add the unclassified status to classifyProviderHttpError if it should map to a richer error

Example fix

// before
const results = await searchKagi({ query, authStorage });
// after
try {
  const results = await searchKagi({ query, authStorage });
} catch (err) {
  if (err instanceof SearchProviderError && err.status === 402) {
    // Kagi subscription issue — check billing
  }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// preflight: verify Kagi key works with a cheap call
const ok = await fetch('https://kagi.com/api/v0/search?q=test', { headers: { Authorization: `Bot ${key}` } }).then(r => r.status !== 401);

Type guard

function isKagiApiError(e: unknown): e is { name: 'KagiApiError'; statusCode?: number; message: string } {
  return e instanceof Error && e.name === 'KagiApiError';
}

Try / catch

try {
  const res = await searchKagi({ query, authStorage });
} catch (err) {
  if (err instanceof SearchProviderError && typeof err.status === 'number') {
    if (err.status === 402) handleBilling();
    else if (err.status >= 500) retryWithBackoff();
    else throw err;
  } else throw err;
}

Prevention

When it happens

Trigger: Kagi API request fails with a KagiApiError carrying a numeric statusCode that classifyProviderHttpError does not map (e.g. 4xx/5xx statuses outside the classified set like 402, 418, 520), so the raw error is wrapped and thrown.

Common situations: Kagi billing/quota states (402 payment required), unusual upstream errors behind Kagi, novel status codes not yet in the classification table.

Related errors


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