{"record":{"id":"ae8f5abe14186e3e","repo":"can1357/oh-my-pi","slug":"anthropic-cache-refresh-request-did-not-expose-a-r","errorCode":null,"errorMessage":"Anthropic cache refresh request did not expose a raw response","messagePattern":"Anthropic cache refresh request did not expose a raw response","errorType":"exception","errorClass":"AnthropicStreamEnvelopeError","httpStatus":null,"severity":"error","filePath":"packages/ai/src/providers/anthropic.ts","lineNumber":2051,"sourceCode":"\t\t\t\trawRequestDump = {\n\t\t\t\t\tprovider: model.provider,\n\t\t\t\t\tapi: output.api,\n\t\t\t\t\tmodel: model.id,\n\t\t\t\t\tmethod: \"POST\",\n\t\t\t\t\turl: `${baseUrl}/v1/messages${isOAuthToken ? \"?beta=true\" : \"\"}`,\n\t\t\t\t\tbody: refreshParams,\n\t\t\t\t};\n\t\t\t\tconst { requestSignal } = activeAbortTracker;\n\t\t\t\tconst requestOptions = {\n\t\t\t\t\t...createSdkStreamRequestOptions(requestSignal, requestTimeoutMs),\n\t\t\t\t\tmaxRetries: 0,\n\t\t\t\t};\n\t\t\t\tconst request: unknown =\n\t\t\t\t\tisOAuthToken && client.beta\n\t\t\t\t\t\t? client.beta.messages.create(refreshParams, requestOptions)\n\t\t\t\t\t\t: client.messages.create(refreshParams, requestOptions);\n\t\t\t\tif (!hasAnthropicRawResponseRequest(request)) {\n\t\t\t\t\tthrow new AIError.AnthropicStreamEnvelopeError(\n\t\t\t\t\t\t\"Anthropic cache refresh request did not expose a raw response\",\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tconst response = await request.asResponse();\n\t\t\t\tawait notifyProviderResponse(options, response, model, response.headers.get(\"request-id\"));\n\t\t\t\tconst body: unknown = await response.json();\n\t\t\t\tif (!isRecord(body)) {\n\t\t\t\t\tthrow new AIError.AnthropicStreamEnvelopeError(\"Anthropic cache refresh returned a malformed response\");\n\t\t\t\t}\n\t\t\t\tconst wireUsage = parseAnthropicWireUsage(body.usage);\n\t\t\t\tif (!wireUsage) {\n\t\t\t\t\tthrow new AIError.AnthropicStreamEnvelopeError(\"Anthropic cache refresh response omitted usage\");\n\t\t\t\t}\n\t\t\t\tif (typeof body.id === \"string\") output.responseId = body.id;\n\t\t\t\toutput.usage.input = wireUsage.input_tokens ?? 0;\n\t\t\t\toutput.usage.output = wireUsage.output_tokens ?? 0;\n\t\t\t\toutput.usage.cacheRead = wireUsage.cache_read_input_tokens ?? 0;\n\t\t\t\toutput.usage.cacheWrite = wireUsage.cache_creation_input_tokens ?? 0;","sourceCodeStart":2033,"sourceCodeEnd":2069,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/ai/src/providers/anthropic.ts#L2033-L2069","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use a supported @anthropic-ai/sdk version where messages.create returns a request exposing asResponse().","Update test mocks to implement asResponse() returning a Response.","Ensure the client isn't wrapped by a proxy that strips methods before reaching the refresh path.","Confirm the OAuth vs non-OAuth branch: both beta and non-beta clients must expose the raw response."],"exampleFix":"// before\ncreate: async (params) => paramsJson // no asResponse\n\n// after\ncreate: async (params) => ({\n  json: paramsJson,\n  asResponse: async () => new Response(JSON.stringify(paramsJson), { headers: { \"request-id\": \"req_1\" } }),\n})","handlingStrategy":"type-guard","validationCode":"const request = client.messages.create(refreshParams, requestOptions);\nif (typeof (request as { asResponse?: unknown }).asResponse !== \"function\") {\n  throw new Error(\"Cache refresh requires an SDK request exposing asResponse(); check SDK version.\");\n}","typeGuard":"function hasRawResponseRequest(req: unknown): req is { asResponse(): Promise<Response> } {\n  return typeof req === \"object\" && req !== null && \"asResponse\" in req && typeof (req as { asResponse?: unknown }).asResponse === \"function\";\n}","tryCatchPattern":"try {\n  const response = await request.asResponse();\n  const body = await response.json();\n  return parseRefreshUsage(body);\n} catch (err) {\n  if (err instanceof AIError.AnthropicStreamEnvelopeError && /did not expose a raw response/.test(err.message)) {\n    // degrade: skip cache-usage refresh for this cycle instead of failing the turn\n  } else {\n    throw err;\n  }\n}","preventionTips":["Keep the raw client instance unwrapped through to the refresh call site.","Include asResponse() in any client test double used with cache-refresh paths.","Align SDK versions across packages that share the Anthropic client.","Cover both the OAuth (beta) and standard client branches in integration tests."],"tags":["anthropic","sdk","cache-refresh","api-mismatch"],"backgroundTag":"sdk-api-mismatch","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}