can1357/oh-my-pi · error · SearchProviderError
Gemini API returned an empty grounded response
Error message
Gemini API returned an empty grounded response
What it means
finalizeGeminiSearchResult() throws this after a Gemini grounded search completes when the result contains neither an answer text nor any grounding sources. Gemini is expected to return a grounded response (answer and/or grounding chunks); an entirely empty result indicates the model returned no usable grounded content, which is treated as a provider failure (HTTP 502).
Source
Thrown at packages/coding-agent/src/web/search/providers/gemini.ts:357
redirect: "manual",
signal: withHardTimeout(signal, 5000),
});
const location = response.headers.get("location");
if (!location) return proxyUrl;
const resolved = new URL(location, proxyUrl);
return resolved.protocol === "http:" || resolved.protocol === "https:" ? resolved.toString() : proxyUrl;
} catch {
return proxyUrl;
}
}
async function finalizeGeminiSearchResult(
result: GeminiSearchResult,
fetchImpl: FetchImpl | undefined,
signal: AbortSignal | undefined,
): Promise<GeminiSearchResult> {
if (!result.answer && result.sources.length === 0) {
throw new SearchProviderError("gemini", "Gemini API returned an empty grounded response", 502);
}
const redirectUrls = new Set<string>();
for (const source of result.sources) {
if (isGroundingRedirectUrl(source.url)) redirectUrls.add(source.url);
}
for (const citation of result.citations) {
if (isGroundingRedirectUrl(citation.url)) redirectUrls.add(citation.url);
}
if (redirectUrls.size === 0) return result;
signal?.throwIfAborted();
const resolvedEntries = await Promise.all(
[...redirectUrls].map(async url => [url, await resolveGroundingRedirect(url, fetchImpl, signal)] as const),
);
signal?.throwIfAborted();
const resolvedUrls = new Map(resolvedEntries);
for (const source of result.sources) {View on GitHub (pinned to 9690622007)
Solutions
- Retry with a rephrased, more specific query — empty grounded responses often follow prompts the model won't ground on
- Inspect the raw API response (log before parsing) to see whether candidates/groundingMetadata were present upstream
- Check safety settings and model configuration; an over-blocked prompt returns empty candidates
- Verify any proxy/gateway between you and Gemini preserves the grounding metadata fields
Defensive patterns
Strategy: fallback
Validate before calling
// No pre-call validation; shape the prompt so grounding is likely:
const safeQuery = query.trim();
if (!safeQuery) throw new Error("Refusing empty Gemini search query"); Type guard
function hasGroundedContent(r: { answer?: string; sources: unknown[] }): boolean {
return Boolean(r.answer && r.answer.trim()) || r.sources.length > 0;
} Try / catch
try {
results = await searchGemini(query);
} catch (err) {
if (err instanceof SearchProviderError && err.message === "Gemini API returned an empty grounded response") {
logger.warn("Gemini grounded search empty — retrying with rephrased query, then fallback", { query });
results = await searchGemini(rephrase(query)).catch(() => fallbackSearch(query));
} else throw err;
} Prevention
- Retry once with a rephrased query before surfacing the failure
- Check upstream raw candidates/groundingMetadata when this recurs to distinguish refusal vs. parsing loss
- Avoid over-restrictive safety settings that empty out candidates
- Have a non-grounded fallback provider for queries Gemini won't ground on
When it happens
Trigger: parseGeminiSearchStream produced a GeminiSearchResult with empty `answer` and zero `sources` — e.g. the model declined/refused to answer, the grounding metadata was empty in the stream, the prompt/query yielded no grounded output, or stream parsing dropped all content.
Common situations: Queries the model refuses or answers with empty candidates, proxy/gateway stripping grounding metadata from the response, overly restrictive safety settings, or a malformed/truncated stream that yielded no chunks.
Related errors
- Gemini API returned no response body
- Devin API error: response body is empty
- Devin auth error: GetUserJwt returned an empty user JWT
- Devin AssignModel error: response carried no assignment JWT
- GitLab Duo Workflow direct_access did not return credentials
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/b6712ff2eab2c6b6.
Report an issue: GitHub.