Bug
_sanitizeMessageForPersistence in AIChatAgent filters out all reasoning parts with empty text. This inadvertently removes Anthropic's redacted_thinking blocks, which are stored by the AI SDK as reasoning parts with empty text + providerMetadata.anthropic.redactedData.
On the next turn, when persisted messages are sent back to the Anthropic API, the latest assistant message is missing its redacted_thinking blocks. The API rejects the request with:
messages.N.content.M: 'thinking' or 'redacted_thinking' blocks in the latest
assistant message cannot be modified. These blocks must remain as they were
in the original response.
Root Cause
In packages/ai-chat/src/ai-chat-agent.ts, the sanitizer:
if (part.type === "reasoning") {
const reasoningPart = part;
if (!reasoningPart.text || reasoningPart.text.trim() === "") return false;
}
This was designed for OpenAI's empty reasoning parts, but it also matches Anthropic's redacted_thinking:
// How @ai-sdk/anthropic stores redacted_thinking:
{
type: "reasoning",
text: "", // ← empty!
providerMetadata: { anthropic: { redactedData: "..." } }
}
Suggested Fix
Check for Anthropic metadata before filtering:
if (part.type === "reasoning") {
const reasoningPart = part;
if (!reasoningPart.text || reasoningPart.text.trim() === "") {
// Preserve Anthropic redacted_thinking (has providerMetadata.anthropic)
if (reasoningPart.providerMetadata?.anthropic) return true;
return false;
}
}
Workaround
Subclasses can patch the instance method in the constructor:
constructor(ctx: DurableObjectState, env: Env) {
super(ctx, env);
(this as any)._sanitizeMessageForPersistence = (message: UIMessage): UIMessage => {
const sanitizedParts = message.parts.filter((part) => {
if (part.type === "reasoning") {
const rp = part as { text?: string; providerMetadata?: { anthropic?: unknown } };
if ((!rp.text || rp.text.trim() === "") && !rp.providerMetadata?.anthropic) {
return false;
}
}
return true;
});
return { ...message, parts: sanitizedParts };
};
}
Environment
@cloudflare/ai-chat: 0.1.3
ai (Vercel AI SDK): v6
@ai-sdk/anthropic: 3.0.37+
- Model: Claude Opus 4.6 with
thinking: { type: "adaptive" }
Bug
_sanitizeMessageForPersistenceinAIChatAgentfilters out all reasoning parts with empty text. This inadvertently removes Anthropic'sredacted_thinkingblocks, which are stored by the AI SDK as reasoning parts with empty text +providerMetadata.anthropic.redactedData.On the next turn, when persisted messages are sent back to the Anthropic API, the latest assistant message is missing its
redacted_thinkingblocks. The API rejects the request with:Root Cause
In
packages/ai-chat/src/ai-chat-agent.ts, the sanitizer:This was designed for OpenAI's empty reasoning parts, but it also matches Anthropic's
redacted_thinking:Suggested Fix
Check for Anthropic metadata before filtering:
Workaround
Subclasses can patch the instance method in the constructor:
Environment
@cloudflare/ai-chat: 0.1.3ai(Vercel AI SDK): v6@ai-sdk/anthropic: 3.0.37+thinking: { type: "adaptive" }