can1357/oh-my-pi · error · AnthropicStreamEnvelopeError

Anthropic cache refresh returned a malformed response

Error message

Anthropic cache refresh returned a malformed response

What it means

This AnthropicStreamEnvelopeError is thrown when the cache-refresh response body parses to something that is not a JSON object (a record). The library expects the standard Anthropic message shape and needs object access to extract usage and response id; anything else (null, array, string, HTML error page) is treated as malformed.

Source

Thrown at packages/ai/src/providers/anthropic.ts:2059

				const { requestSignal } = activeAbortTracker;
				const requestOptions = {
					...createSdkStreamRequestOptions(requestSignal, requestTimeoutMs),
					maxRetries: 0,
				};
				const request: unknown =
					isOAuthToken && client.beta
						? client.beta.messages.create(refreshParams, requestOptions)
						: client.messages.create(refreshParams, requestOptions);
				if (!hasAnthropicRawResponseRequest(request)) {
					throw new AIError.AnthropicStreamEnvelopeError(
						"Anthropic cache refresh request did not expose a raw response",
					);
				}
				const response = await request.asResponse();
				await notifyProviderResponse(options, response, model, response.headers.get("request-id"));
				const body: unknown = await response.json();
				if (!isRecord(body)) {
					throw new AIError.AnthropicStreamEnvelopeError("Anthropic cache refresh returned a malformed response");
				}
				const wireUsage = parseAnthropicWireUsage(body.usage);
				if (!wireUsage) {
					throw new AIError.AnthropicStreamEnvelopeError("Anthropic cache refresh response omitted usage");
				}
				if (typeof body.id === "string") output.responseId = body.id;
				output.usage.input = wireUsage.input_tokens ?? 0;
				output.usage.output = wireUsage.output_tokens ?? 0;
				output.usage.cacheRead = wireUsage.cache_read_input_tokens ?? 0;
				output.usage.cacheWrite = wireUsage.cache_creation_input_tokens ?? 0;
				applyAnthropicUsageExtras(output.usage, wireUsage);
				output.usage.totalTokens =
					output.usage.input + output.usage.output + output.usage.cacheRead + output.usage.cacheWrite;
				calculateCost(model, output.usage);
				output.duration = performance.now() - startTime;
				stream.push({ type: "start", partial: output });
				stream.push({ type: "done", reason: "stop", message: output });
				stream.end();

View on GitHub (pinned to 9690622007)

Solutions

  1. Verify the base URL and network path point at the real Anthropic API (or a correct gateway).
  2. Log/inspect the raw response body and status to see what the endpoint actually returned.
  3. Check proxy/gateway configuration for interception (auth redirects, HTML error pages).
  4. Fix test mocks to return a valid message-shaped JSON object including usage.

Example fix

// before
asResponse: async () => new Response("<html>Service Unavailable</html>")

// after
asResponse: async () => new Response(JSON.stringify({
  id: "msg_1", type: "message", role: "assistant",
  usage: { input_tokens: 10, output_tokens: 5 },
}), { headers: { "content-type": "application/json" } })
Defensive patterns

Strategy: validation

Validate before calling

const body: unknown = await response.json();
if (typeof body !== "object" || body === null || Array.isArray(body)) {
  throw new Error(`Cache refresh returned non-object JSON: ${typeof body}; verify the endpoint/base URL.`);
}

Type guard

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null && !Array.isArray(value);
}

Try / catch

try {
  return await refreshCacheUsage(params);
} catch (err) {
  if (err instanceof AIError.AnthropicStreamEnvelopeError && /malformed response/.test(err.message)) {
    logger.warn("Cache refresh got malformed body; keeping previous usage", { status: err });
    return lastKnownUsage;
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: request.asResponse() succeeds but response.json() yields a non-object — e.g. a gateway/proxy returning an HTML error page with 200, an empty body, or a JSON array/string.

Common situations: Corporate proxies or API gateways intercepting the request and returning login/error pages; misconfigured base URL pointing at a non-Anthropic endpoint; CDN error responses; mock returning invalid payloads.

Understand the failure class

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/d20595b9abf577c5. Report an issue: GitHub.