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

  1. Log response.status and the raw text of the body (read it before calling response.json()) to see what the server actually returned
  2. Verify the Anthropic base URL and that no proxy is intercepting requests to api.anthropic.com
  3. Check the Anthropic status page for outages and retry with backoff for 5xx responses
  4. 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

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.

Related errors


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)