thedotmack/claude-mem · error · ServerClassifiedProviderError
parse_error
parse_error
Error message
Anthropic returned invalid JSON
What it means
The Anthropic provider attempted `response.json()` on the HTTP response from the Anthropic Messages API and the body was not valid JSON. The provider wraps the underlying parse failure in a ServerClassifiedProviderError with kind 'parse_error' so callers can distinguish malformed transport payloads from API-level errors. This is thrown from `generate` when the raw HTTP body cannot be deserialized into AnthropicMessagesResponse.
Solutions
- Log response.status and the raw text of the body (read it before calling response.json()) to see what the server actually returned
- Verify the Anthropic base URL and that no proxy is intercepting requests to api.anthropic.com
- Check the Anthropic status page for outages and retry with backoff for 5xx responses
- Catch ServerClassifiedProviderError with kind 'parse_error' and surface a clear message to the user rather than crashing
Example fix
// before: json() throws on HTML proxy response
data = (await response.json()) as AnthropicMessagesResponse;
// after: check status and read text first
const raw = await response.text();
if (!response.ok) throw new ServerClassifiedProviderError(`HTTP ${response.status}: ${raw.slice(0, 200)}`, { kind: 'api_error' });
data = JSON.parse(raw) as AnthropicMessagesResponse; Defensive patterns
Strategy: try-catch
Validate before calling
if (!process.env.ANTHROPIC_API_KEY) throw new Error('ANTHROPIC_API_KEY is not set'); Type guard
function isAnthropicMessagesResponse(v: unknown): v is AnthropicMessagesResponse {
return typeof v === 'object' && v !== null &&
(Array.isArray((v as any).content) || (v as any).error !== undefined);
} Try / catch
try {
const result = await provider.generate(input);
} catch (e) {
if (e instanceof ServerClassifiedProviderError && e.kind === 'parse_error') {
log.warn('Anthropic returned non-JSON; retrying or falling back', { cause: e.cause });
return fallbackProvider.generate(input);
}
throw e;
} Prevention
- Read the raw body text and check response.status before parsing
- Configure proxy/gateway to pass api.anthropic.com traffic through unchanged
- Monitor the Anthropic status page and add backoff retry for 5xx
- Keep a fallback provider for parse failures
When it happens
Trigger: Calling ClaudeObservationProvider.generate() when the Anthropic endpoint returns a non-JSON body: HTML error pages from proxies/CDNs, empty bodies on 502/504, truncated responses, or a wrong base URL pointing at a non-API server.
Common situations: Corporate proxies or gateways returning HTML '502 Bad Gateway' pages; Cloudflare challenge pages; hitting the wrong URL (e.g. a login page) after a misconfigured base URL; network middleboxes truncating streamed responses; Anthropic outage returning plain-text error pages.
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
- OpenRouter returned invalid JSON
- parse_error
- Anthropic API error
- Anthropic API error
- Anthropic API rejected request with HTTP 400: this model…
AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17).
Data as JSON: /api/errors/0de29a7bdf61354d.
Report an issue: GitHub.
Appendix: 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 d8bc9755e7)