can1357/oh-my-pi · error · SearchProviderError
Failed to parse Z.AI MCP response
Error message
Failed to parse Z.AI MCP response
What it means
This SearchProviderError is thrown by parseZaiMcpResponse when the HTTP response body from Z.AI's MCP endpoint is neither a valid SSE stream (no 'data:' lines with JSON payloads) nor a valid JSON document. The provider attempted SSE-style extraction first, then a whole-body JSON.parse fallback, and both failed, meaning the body is HTML, empty, or otherwise non-JSON text returned with a 2xx status.
Source
Thrown at packages/coding-agent/src/web/search/providers/zai.ts:110
function parseZaiMcpResponse(rawText: string): unknown {
const parsedMessages: unknown[] = [];
for (const line of rawText.split("\n")) {
const trimmed = line.trim();
if (!trimmed.startsWith("data:")) continue;
const data = trimmed.slice(5).trim();
if (!data) continue;
try {
parsedMessages.push(JSON.parse(data));
} catch {
// Ignore non-JSON data events.
}
}
if (parsedMessages.length === 0) {
try {
parsedMessages.push(JSON.parse(rawText));
} catch {
throw new SearchProviderError("zai", "Failed to parse Z.AI MCP response", 500);
}
}
return parsedMessages[parsedMessages.length - 1];
}
async function postZaiMcp(
apiKey: string,
method: string,
params: Record<string, unknown>,
sessionId: string | undefined,
signal: AbortSignal | undefined,
fetchImpl: FetchImpl,
expectResponse: boolean,
timeoutMs?: number,
): Promise<ZaiMcpPostResult> {
const headers: Record<string, string> = {
Authorization: `Bearer ${apiKey}`,View on GitHub (pinned to 9690622007)
Solutions
- Inspect the raw response body: log the response text and Content-Type header right before parseZaiMcpResponse is called to see what the server actually returned.
- Check for a proxy, VPN, or captive portal intercepting api.z.ai traffic and bypass or disable it.
- Verify Z.AI service status / the MCP endpoint URL (ZAI_MCP_URL) has not changed or moved.
- If you pass a custom fetch implementation, ensure it returns the real JSON/SSE body rather than an HTML mock or error page.
Example fix
// before: custom fetch swallows real response
const fetch = async () => new Response(await Bun.file('fixture.html').text(), { status: 200 });
// after: mock must return valid JSON-RPC body
const fetch = async () => Response.json({ jsonrpc: '2.0', id: 1, result: {} }); Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-validate the response shape before trusting the search result:
const resp = await fetchImpl(url, init);
const ct = resp.headers.get('content-type') ?? '';
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
if (!/json|event-stream/i.test(ct)) throw new Error(`Unexpected content-type: ${ct}`); Type guard
function isJsonRpcLike(v: unknown): v is Record<string, unknown> {
return typeof v === 'object' && v !== null && !Array.isArray(v);
} Try / catch
try {
const result = await searchZai({ query, authStorage });
} catch (err) {
if (err instanceof SearchProviderError && err.message.includes('Failed to parse Z.AI MCP response')) {
// Non-JSON body with HTTP 200: fall back to another search provider.
return fallbackSearch(query);
}
throw err;
} Prevention
- Check the Content-Type header of responses from api.z.ai before parsing.
- Bypass corporate proxies/VPNs for api.z.ai or add it to proxy exclusions.
- Keep custom fetch mocks returning valid JSON-RPC or SSE data payloads.
- Monitor Z.AI endpoint contract changes; pin and review provider updates.
When it happens
Trigger: postZaiMcp received response.ok === true from https://api.z.ai/api/mcp/web_search_prime/mcp, but the body text contains no parseable 'data:' SSE events and JSON.parse(rawText) throws. Typical bodies: an HTML login/interstitial page served behind a proxy, an empty body, a plain-text quota notice, or an unexpected content-type (e.g. text/html) from the endpoint.
Common situations: Corporate proxy or captive portal intercepting api.z.ai and returning HTML with status 200; Z.AI changing the MCP endpoint contract or serving a maintenance page; a misconfigured fetch override (params.fetch) in tests that returns mock HTML; region blocks redirecting to an HTML error page.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- HTTP ${response.status} resuming MCP SSE stream: ${text}
- MCP SSE resume returned unsupported Content-Type: ${contentT
- SSE response did not include a body
- No response received for request ID ${expectedId}
- V2 remote compaction failed (${response.status} ${response.s
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/c1f991e3c01c3597.
Report an issue: GitHub.