{"record":{"id":"cd99c1f1f084750b","repo":"langchain-ai/langchain","slug":"expected-openai-role-to-be-a-str-got-type","errorCode":null,"errorMessage":"Expected '__openai_role__' to be a str, got {type(role).__name__}","messagePattern":"Expected '__openai_role__' to be a str, got (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/messages/utils.py","lineNumber":2220,"sourceCode":"    return message.type in types_str or isinstance(message, types_types)\n\n\ndef _bytes_to_b64_str(bytes_: bytes) -> str:\n    return base64.b64encode(bytes_).decode(\"utf-8\")\n\n\ndef _get_message_openai_role(message: BaseMessage) -> str:\n    if isinstance(message, AIMessage):\n        return \"assistant\"\n    if isinstance(message, HumanMessage):\n        return \"user\"\n    if isinstance(message, ToolMessage):\n        return \"tool\"\n    if isinstance(message, SystemMessage):\n        role = message.additional_kwargs.get(\"__openai_role__\", \"system\")\n        if not isinstance(role, str):\n            msg = f\"Expected '__openai_role__' to be a str, got {type(role).__name__}\"\n            raise TypeError(msg)\n        return role\n    if isinstance(message, FunctionMessage):\n        return \"function\"\n    if isinstance(message, ChatMessage):\n        return message.role\n    msg = f\"Unknown BaseMessage type {message.__class__}.\"\n    raise ValueError(msg)\n\n\ndef _convert_to_openai_tool_calls(tool_calls: list[ToolCall]) -> list[dict[str, Any]]:\n    return [\n        {\n            \"type\": \"function\",\n            \"id\": tool_call[\"id\"],\n            \"function\": {\n                \"name\": tool_call[\"name\"],\n                \"arguments\": json.dumps(tool_call[\"args\"], ensure_ascii=False),\n            },","sourceCodeStart":2202,"sourceCodeEnd":2238,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/messages/utils.py#L2202-L2238","documentation":"When determining an OpenAI role, langchain-core lets a SystemMessage override its default `system` role via `additional_kwargs[\"__openai_role__\"]` (used e.g. to emit `developer` messages). This value must be a string; any other type raises TypeError immediately. The strictness exists because the role is placed verbatim into the request payload, and a non-string would corrupt the API call.","triggerScenarios":"`SystemMessage(\"...\", additional_kwargs={\"__openai_role__\": [\"developer\"]})` or `\"__openai_role__\": 1}`; programmatically setting the kwarg from unvalidated config/JSON where it arrives as a list or None.","commonSituations":"Loading prompt templates from YAML/JSON where `__openai_role__` was written as a one-element array; threading user-supplied options into additional_kwargs without type checks.","solutions":["Set the value as a plain string: `additional_kwargs={\"__openai_role__\": \"developer\"}`.","Validate/coerce the config value (`str(value)`) before constructing the SystemMessage.","If you do not need a custom role, remove `__openai_role__` entirely — `system` is the default."],"exampleFix":"// before\nSystemMessage(\"You are helpful.\", additional_kwargs={\"__openai_role__\": [\"developer\"]})\n\n// after\nSystemMessage(\"You are helpful.\", additional_kwargs={\"__openai_role__\": \"developer\"})","handlingStrategy":"type-guard","validationCode":"role = msg.additional_kwargs.get(\"__openai_role__\")\nif role is not None and not isinstance(role, str):\n    raise TypeError(f\"__openai_role__ must be str, got {type(role).__name__}\")\n# or coerce: msg.additional_kwargs[\"__openai_role__\"] = str(role)","typeGuard":"def has_valid_openai_role(msg) -> bool:\n    role = msg.additional_kwargs.get(\"__openai_role__\")\n    return role is None or isinstance(role, str)","tryCatchPattern":"try:\n    _get_message_openai_role(msg)  # or the conversion call\nexcept TypeError as e:\n    if \"__openai_role__\" in str(e):\n        msg.additional_kwargs[\"__openai_role__\"] = str(msg.additional_kwargs[\"__openai_role__\"])\n        retry()","preventionTips":["Validate config-driven additional_kwargs against a small schema","Write __openai_role__ as a bare string in YAML/JSON templates","Omit __openai_role__ unless you specifically need a non-system role"],"tags":["messages","openai","system-role","type-error","validation"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}