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
- Verify the base URL and network path point at the real Anthropic API (or a correct gateway).
- Log/inspect the raw response body and status to see what the endpoint actually returned.
- Check proxy/gateway configuration for interception (auth redirects, HTML error pages).
- 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
- Verify ANTHROPIC_BASE_URL points at the real API; capture raw bodies on unexpected responses.
- Bypass or correctly configure corporate proxies that inject HTML error pages.
- Log response content-type and first bytes when a 200 response is not application/json.
- Mock refresh responses with realistic message-shaped JSON in tests.
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
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Anthropic cache refresh request did not expose a raw respons
- Anthropic cache refresh response omitted usage
- Invalid cmux socket JSON response: ${err instanceof Error ?
- Replacement text is not valid UTF-8: {err}
- V2 compaction stream parse failed: ${err instanceof Error ?
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d20595b9abf577c5.
Report an issue: GitHub.