can1357/oh-my-pi · error · AnthropicStreamEnvelopeError

Anthropic cache refresh request did not expose a raw respons

Error message

Anthropic cache refresh request did not expose a raw response

What it means

This AnthropicStreamEnvelopeError is thrown during cache refresh when the request promise returned by the Anthropic client lacks the asResponse() method, so the library cannot access the raw HTTP response. The cache-refresh flow must read the raw JSON body and notify listeners with response metadata, which requires the raw response surface.

Source

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

				rawRequestDump = {
					provider: model.provider,
					api: output.api,
					model: model.id,
					method: "POST",
					url: `${baseUrl}/v1/messages${isOAuthToken ? "?beta=true" : ""}`,
					body: refreshParams,
				};
				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;

View on GitHub (pinned to 9690622007)

Solutions

  1. Use a supported @anthropic-ai/sdk version where messages.create returns a request exposing asResponse().
  2. Update test mocks to implement asResponse() returning a Response.
  3. Ensure the client isn't wrapped by a proxy that strips methods before reaching the refresh path.
  4. Confirm the OAuth vs non-OAuth branch: both beta and non-beta clients must expose the raw response.

Example fix

// before
create: async (params) => paramsJson // no asResponse

// after
create: async (params) => ({
  json: paramsJson,
  asResponse: async () => new Response(JSON.stringify(paramsJson), { headers: { "request-id": "req_1" } }),
})
Defensive patterns

Strategy: type-guard

Validate before calling

const request = client.messages.create(refreshParams, requestOptions);
if (typeof (request as { asResponse?: unknown }).asResponse !== "function") {
  throw new Error("Cache refresh requires an SDK request exposing asResponse(); check SDK version.");
}

Type guard

function hasRawResponseRequest(req: unknown): req is { asResponse(): Promise<Response> } {
  return typeof req === "object" && req !== null && "asResponse" in req && typeof (req as { asResponse?: unknown }).asResponse === "function";
}

Try / catch

try {
  const response = await request.asResponse();
  const body = await response.json();
  return parseRefreshUsage(body);
} catch (err) {
  if (err instanceof AIError.AnthropicStreamEnvelopeError && /did not expose a raw response/.test(err.message)) {
    // degrade: skip cache-usage refresh for this cycle instead of failing the turn
  } else {
    throw err;
  }
}

Prevention

When it happens

Trigger: client.messages.create (or client.beta.messages.create) returns an object failing hasAnthropicRawResponseRequest — e.g. an incompatible SDK version, a mock without asResponse(), or a wrapped/proxied client during a non-streaming cache-refresh call.

Common situations: Older/forked @anthropic-ai/sdk without asResponse on non-streaming creates; test doubles that only implement the happy-path shape; middleware replacing the client; swapping to a compatible-but-different client library.

Related errors


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