can1357/oh-my-pi · error · Error
Exa MCP search returned unexpected response shape.
Error message
Exa MCP search returned unexpected response shape.
What it means
The Exa MCP call succeeded at the transport level and the result was not flagged as an error, but the payload did not match the expected ExaSearchResponse shape and could not be normalized/parsed into one (normalizeExaMcpPayload, isSearchResponse, and parseExaMcpTextPayload all failed). Thrown as a plain Error indicating a contract violation from the MCP endpoint.
Source
Thrown at packages/coding-agent/src/web/search/providers/exa.ts:426
throw new Error(`MCP error: ${mcpResponse.error.message}`);
}
if (mcpResponse.result?.isError) {
const message = mcpResponse.result.content
?.find(item => item.type === "text" && typeof item.text === "string")
?.text?.trim();
throw new SearchProviderError("exa", message || "Exa MCP returned an error");
}
const responsePayload = normalizeExaMcpPayload(mcpResponse.result);
if (isSearchResponse(responsePayload)) {
return responsePayload as ExaSearchResponse;
}
const parsed = parseExaMcpTextPayload(responsePayload);
if (parsed) {
return parsed;
}
throw new Error("Exa MCP search returned unexpected response shape.");
}
/** Execute Exa web search */
export async function searchExa(params: ExaSearchParams): Promise<SearchResponse> {
// AuthStorage-backed key takes precedence (existing behavior); probe it once
// so the env-key and keyless-MCP fallbacks below stay intact, then drive the
// authStorage path through the central force-refresh/rotate retry policy.
const storedKey = params.authStorage
? await params.authStorage.getApiKey("exa", params.sessionId, { signal: params.signal })
: undefined;
const keyOrResolver: ApiKey | undefined =
storedKey && params.authStorage
? params.authStorage.resolver("exa", { sessionId: params.sessionId })
: getEnvApiKey("exa");
const response = keyOrResolver
? await withAuth(keyOrResolver, key => callExaSearch(key, params), { signal: params.signal })
: await callExaMcpSearch(params);
View on GitHub (pinned to 9690622007)
Solutions
- Check for a client/package update that matches the current Exa MCP schema.
- Inspect the raw mcpResponse.result payload to identify the new shape and update normalizeExaMcpPayload/isSearchResponse accordingly.
- Pin/verify the MCP endpoint version if Exa exposes one.
- Fall back to the REST API path or another search provider.
Example fix
// before
const results = await searchExa({ query }); // keyless MCP path, new schema -> throws
// after
const results = await searchExa({ query, apiKey: process.env.EXA_API_KEY }); // stable REST path Defensive patterns
Strategy: validation
Validate before calling
function looksLikeExaSearchResult(r: unknown): boolean {
const o = r as { results?: unknown[] } | null;
return !!o && Array.isArray(o.results);
} Type guard
function isExaSearchResponse(x: unknown): x is ExaSearchResponse {
return typeof x === "object" && x !== null && "results" in x && Array.isArray((x as ExaSearchResponse).results);
} Try / catch
try {
const res = await searchExa({ query });
} catch (e) {
if (e instanceof Error && e.message.includes("unexpected response shape")) {
// schema drift: update client or fall back to REST provider
} else throw e;
} Prevention
- Keep the Exa client package updated to track MCP schema changes
- Validate payloads with isSearchResponse before consuming results
- Pin or verify the MCP endpoint version in production
- Prefer the versioned REST API when schema stability matters
When it happens
Trigger: Exa changed its MCP result schema (renamed fields, nested differently); the result contained a different tool's payload; an empty/odd result object passed all error checks but failed shape validation.
Common situations: Exa MCP API version drift without a corresponding client update; the agent requesting a tool whose payload shape differs from web_search; caching layers returning stale/reshaped payloads.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Failed to parse MCP response
- Exa MCP request failed (${response.status}): ${errorText}
- MCP error: ${mcpResponse.error.message}
- Exa MCP returned an error (falls back to `${message}` when p
- Exa API error (${response.status}): ${errorText}
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/653b92bb3bf5a183.
Report an issue: GitHub.