thedotmack/claude-mem · error · ServerClassifiedProviderError
parse_error
parse_error
Error message
Anthropic returned invalid JSON
What it means
Thrown by ClaudeObservationProvider.generate() when response.json() rejects after a 2xx HTTP response from Anthropic. Classified as parse_error, it indicates the body was not valid JSON — typically an HTML error page, an empty body, or a truncated/intercepted response. The thrown error carries the underlying parse error as its cause.
Source
Thrown at src/server/generation/providers/ClaudeObservationProvider.ts:98
});
}
if (!response.ok) {
const bodyText = await safeReadBody(response);
throw classifyClaudeServerError({
status: response.status,
bodyText,
headers: response.headers,
cause: new Error(`Anthropic API error: ${response.status} - ${bodyText}`),
});
}
let data: AnthropicMessagesResponse;
try {
data = (await response.json()) as AnthropicMessagesResponse;
} catch (parseError) {
const err = parseError instanceof Error ? parseError : new Error(String(parseError));
throw new ServerClassifiedProviderError('Anthropic returned invalid JSON', {
kind: 'parse_error',
cause: err,
});
}
if (data.error) {
throw classifyClaudeServerError({
status: response.status,
bodyText: `${data.error.type ?? ''} ${data.error.message ?? ''}`,
headers: response.headers,
cause: new Error(`Anthropic API error: ${data.error.type} - ${data.error.message}`),
});
}
const blocks = Array.isArray(data.content) ? data.content : [];
const rawText = blocks
.filter(block => block?.type === 'text' && typeof block.text === 'string')
.map(block => block.text!)View on GitHub (pinned to d768ba3643)
Solutions
- Inspect the caught error's cause and the prior status/bodyText logged just before the parse — non-2xx is handled separately, so a 2xx with bad JSON points at interception.
- Re-run; transient HTML interception (WAF/proxy) often clears on retry.
- If behind a proxy, bypass it for api.anthropic.com or add it to the allowlist.
- Confirm the configured base URL resolves to the real Anthropic API host.
- If persistent, capture the raw body to confirm whether it is HTML/empty, then report upstream.
Example fix
// before: only status checked, JSON parse throws unexpectedly
// after: capture bodyText for diagnostics before parsing
const bodyText = await response.text();
if (!bodyText.trim().startsWith('{')) {
throw new Error('Anthropic returned non-JSON body');
}
const data = JSON.parse(bodyText) as AnthropicMessagesResponse; Defensive patterns
Strategy: retry
Validate before calling
async function fetchJsonOrThrow(response: Response): Promise<unknown> {
const bodyText = await response.text();
if (!response.ok) {
throw new Error(`Anthropic API error: ${response.status} - ${bodyText}`);
}
if (!bodyText || !bodyText.trim().startsWith('{')) {
throw new Error('Anthropic returned a non-JSON body (possible proxy interception)');
}
return JSON.parse(bodyText);
} Type guard
function isAnthropicMessagesResponse(v: unknown): v is AnthropicMessagesResponse {
return typeof v === 'object' && v !== null
&& Array.isArray((v as { content?: unknown }).content);
} Try / catch
for (let attempt = 0; attempt < 3; attempt++) {
try {
return await provider.generate(context);
} catch (error) {
const isParse = error instanceof ServerClassifiedProviderError && error.kind === 'parse_error';
if (!isParse || attempt === 2) throw error;
await new Promise(r => setTimeout(r, 2 ** attempt * 500)); // backoff
}
} Prevention
- Read the body as text first and inspect its prefix before JSON.parse to distinguish interception from a real parse bug.
- Bypass corporate proxies for the provider host or add it to an allowlist.
- Verify the configured base URL resolves to the real Anthropic API.
- Retry parse errors with exponential backoff since interception is often transient.
When it happens
Trigger: A corporate proxy or WAF returns an HTML block page instead of JSON; the upstream returns 200 with an empty or partial body; a gateway truncates the stream; a rate-limit intermediary returns a non-JSON challenge; the endpoint URL points at the wrong host.
Common situations: Behind a corporate proxy that rewrites responses. Anthropic serves a temporary HTML status page during an incident. A misconfigured base URL hits a CDN edge returning HTML. Network instability truncates the body mid-stream.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- parse_error
- parse_error
- auth_invalid
- SSE stream returned HTTP ${response.status}
- plugin/.mcp.json mcp-search launcher must include Claude cac
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/0de29a7bdf61354d.
Report an issue: GitHub.