{"record":{"id":"9e1f722144669323","repo":"BerriAI/litellm","slug":"unable-to-parse-anthropic-tool-result-for-message","errorCode":null,"errorMessage":"Unable to parse anthropic tool result for message: {message}","messagePattern":"Unable to parse anthropic tool result for message: (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/prompt_templates/factory.py","lineNumber":1692,"sourceCode":"        anthropic_tool_result = AnthropicMessagesToolResultParam(\n            type=\"tool_result\",\n            tool_use_id=sanitized_tool_use_id,\n            content=anthropic_content,\n        )\n\n    if message[\"role\"] == \"function\":\n        function_message: Final[ChatCompletionFunctionMessage] = message\n        tool_call_id = function_message.get(\"tool_call_id\") or str(uuid.uuid4())\n        # Sanitize tool_use_id to match Anthropic's pattern requirement: ^[a-zA-Z0-9_-]+$\n        sanitized_tool_use_id = _sanitize_anthropic_tool_use_id(tool_call_id)\n        anthropic_tool_result = AnthropicMessagesToolResultParam(\n            type=\"tool_result\",\n            tool_use_id=sanitized_tool_use_id,\n            content=anthropic_content,\n        )\n\n    if anthropic_tool_result is None:\n        raise Exception(f\"Unable to parse anthropic tool result for message: {message}\")\n    if cache_control is not None:\n        anthropic_tool_result[\"cache_control\"] = cache_control\n    return anthropic_tool_result\n\n\ndef convert_function_to_anthropic_tool_invoke(\n    function_call: dict | ChatCompletionToolCallFunctionChunk,\n) -> list[AnthropicMessagesToolUseParam]:\n    try:\n        _name: Final = get_attribute_or_key(function_call, \"name\") or \"\"\n        _arguments: Final = get_attribute_or_key(function_call, \"arguments\")\n\n        tool_input: Final = parse_tool_call_arguments(\n            _arguments, tool_name=_name, context=\"Anthropic function to tool invoke\"\n        )\n\n        anthropic_tool_invoke: Final = [\n            AnthropicMessagesToolUseParam(","sourceCodeStart":1674,"sourceCodeEnd":1710,"githubUrl":"https://github.com/BerriAI/litellm/blob/6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d/litellm/litellm_core_utils/prompt_templates/factory.py#L1674-L1710","documentation":"In Anthropic tool-result conversion: after handling role='tool' and role='function' messages, anthropic_tool_result is still None, meaning the message did not fit any supported tool-result shape (unsupported role, missing content, or content blocks the converter cannot map to tool_result). LiteLLM includes the entire message so you can see what failed.","triggerScenarios":"A message with role='tool' but empty/unparseable content, unexpected content block types, or a role that reached this function without matching the tool/function branches; sending tool results to anthropic/claude models where the content list contains unsupported block types.","commonSituations":"Agent frameworks returning None or empty-string tool output; tool responses containing nested content lists instead of plain text/JSON; hand-assembled messages with role typos like 'Tool'.","solutions":["Make each tool message a plain dict: {\"role\": \"tool\", \"tool_call_id\": <id>, \"content\": <str or simple blocks>}","Coerce tool output to a non-empty string (str(result) or json.dumps(result)) before appending","Verify the preceding assistant message has the matching tool_call id"],"exampleFix":"# before\nmessages.append({\"role\": \"tool\", \"tool_call_id\": call_id, \"content\": tool_output})  # tool_output may be None\n\n# after\nmessages.append({\"role\": \"tool\", \"tool_call_id\": call_id,\n                  \"content\": json.dumps(tool_output) if tool_output is not None else \"(no output)\"})","handlingStrategy":"validation","validationCode":"def is_valid_tool_message(m: dict) -> bool:\n    return (\n        m.get(\"role\") in (\"tool\", \"function\")\n        and bool(m.get(\"tool_call_id\"))\n        and bool(m.get(\"content\"))\n    )","typeGuard":"from typing import Any\n\ndef is_anthropic_safe_tool_message(m: Any) -> bool:\n    return (\n        isinstance(m, dict)\n        and m.get(\"role\") in (\"tool\", \"function\")\n        and isinstance(m.get(\"content\"), (str, list))\n        and len(m.get(\"content\") or \"\") > 0\n    )","tryCatchPattern":null,"preventionTips":["Coerce tool output to a non-empty string before appending (json.dumps or str)","Always pair tool messages with their assistant tool_call by id","Never append None tool results"],"tags":["anthropic","tool-result","messages","validation"],"backgroundTag":null,"analyzedSha":"6c2dcb801bf2b75c18f1bb24140e7cf57465cc4d","analyzedAt":"2026-08-15T07:12:03.035Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}