{"record":{"id":"4e1f5cd469e2c521","repo":"sgl-project/sglang","slug":"anthropic-redacted-thinking-history-is-not-support","errorCode":null,"errorMessage":"Anthropic redacted_thinking history is not supported","messagePattern":"Anthropic redacted_thinking history is not supported","errorType":"http","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"python/sglang/srt/entrypoints/anthropic/serving.py","lineNumber":386,"sourceCode":"\n        def _convert_assistant_thinking_blocks(\n            blocks: list[AnthropicContentBlock],\n        ) -> tuple[Optional[str], Optional[str]]:\n            \"\"\"Reconstruct prior-turn thinking as ``(reasoning_content, text)``.\n\n            At most one is set: encoders that frame the reasoning channel take\n            it as ``reasoning_content``, everything else gets it re-wrapped and\n            spliced into content.\n\n            ``redacted_thinking`` carries encrypted bytes that no local\n            parser can interpret, so we raise rather than silently drop it.\n            On non-reasoning models (no detector configured) the rewrap is\n            best-effort: we log a warning and drop the thinking text so a\n            history echo doesn't 400 the whole request — the prior thinking\n            is opaque context the model didn't need anyway.\n            \"\"\"\n            if any(block.type == \"redacted_thinking\" for block in blocks):\n                raise ValueError(\"Anthropic redacted_thinking history is not supported\")\n\n            thinking_parts = [\n                block.thinking\n                for block in blocks\n                if block.type == \"thinking\" and block.thinking\n            ]\n            if not thinking_parts:\n                return None, None\n\n            reasoning_text = \"\\n\".join(thinking_parts)\n            if self.openai_serving_chat.supports_native_reasoning_history():\n                return reasoning_text, None\n\n            try:\n                return None, self.openai_serving_chat.wrap_reasoning_history(\n                    reasoning_text\n                )\n            except ValueError as e:","sourceCodeStart":368,"sourceCodeEnd":404,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/entrypoints/anthropic/serving.py#L368-L404","documentation":"Raised while converting an Anthropic request to the internal chat-completion format: an assistant message in the conversation history contains a redacted_thinking block (Anthropic's encrypted, opaque thinking blocks), which SGLang cannot re-encode because it lacks Anthropic's encryption keys/context. The whole request is rejected rather than silently corrupting history.","triggerScenarios":"POST /v1/messages where messages[] contains an assistant turn whose content includes {\"type\": \"redacted_thinking\", \"data\": \"...\"} — typically captured from a previous real Anthropic API response that was replayed as history.","commonSituations":"Replaying or continuing conversations originally served by Anthropic's API (which emits redacted_thinking for some safety-flagged thinking); logging request/response pairs and feeding them back; agent frameworks that store raw Anthropic responses as history.","solutions":["Strip redacted_thinking blocks from assistant history before sending (the code already drops plain 'thinking' text best-effort; redacted blocks must be removed client-side)","Regenerate the history from plain text/transcript instead of raw Anthropic response objects","If using an Anthropic SDK passthrough of prior responses, filter content blocks to types text/tool_use only"],"exampleFix":"# before\nmessages = [{\"role\": \"assistant\", \"content\": prior_anthropic_response_content}]\n# after\nmessages = [{\n  \"role\": \"assistant\",\n  \"content\": [b for b in prior_anthropic_response_content\n               if b.get(\"type\") not in (\"redacted_thinking\", \"thinking\")]\n}]","handlingStrategy":"validation","validationCode":"SUPPORTED = {\"text\", \"tool_use\", \"image\", \"document\"}\nfor msg in messages:\n    if msg.get(\"role\") == \"assistant\" and isinstance(msg.get(\"content\"), list):\n        msg[\"content\"] = [b for b in msg[\"content\"] if b.get(\"type\") in SUPPORTED]","typeGuard":"def has_redacted_thinking(messages) -> bool:\n    return any(b.get(\"type\") == \"redacted_thinking\"\n               for m in messages if isinstance(m.get(\"content\"), list)\n               for b in m[\"content\"])","tryCatchPattern":"try: resp = client.messages.create(...)\nexcept ValueError as e:\n    if 'redacted_thinking' in str(e): strip_history_and_retry()","preventionTips":["Never replay raw Anthropic response blocks as history","Store a normalized transcript, not the API response"],"tags":["anthropic","redacted-thinking","conversation-history","request-conversion"],"backgroundTag":"unsupported-history-block","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}