You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Python: Bug: An empty tool result becomes the string 'None' after ChatHistory.to_prompt() / from_rendered_prompt() #14685
original tool message:
result field: ''
to_dict(): {'role': 'tool', 'content': '', 'tool_call_id': 'call_1'}
after to_prompt() -> from_rendered_prompt():
result field: None
to_dict(): {'role': 'tool', 'content': 'None', 'tool_call_id': 'call_1'}
rendered template: <chat_history>...<message role="tool"><function_result id="call_1" /></message></chat_history>
The empty element parses to result=None, and str(None) is what gets sent to the model. The same happens through the Jinja2 messages helper ({{ messages(chat_history) }}) and through KernelFunctionFromPrompt, which performs this round trip for every chat completion. JSON serialize() / restore_chat_history() preserves "", so only the XML prompt path is affected.
Root cause: FunctionResultContent.from_element sets result=element.text (python/semantic_kernel/contents/function_result_content.py:119), which is None for an empty element; ChatMessageContent._parse_items then stringifies it (python/semantic_kernel/contents/chat_message_content.py:335-336).
To Reproduce
Empty tool results are supported since #14472 (ChatHistory.add_tool_message("")). Run from python/:
uv run python repro_tool_result_roundtrip.py
Expected behavior
An empty result stays "" through to_prompt() / from_rendered_prompt(); to_dict() should keep content='', not 'None'. (Alternatively, rendering could emit an explicit empty text so parsing round-trips.)
Platform
Language: Python
Source: main branch at 4bb64ee (2026-10-28), semantic-kernel 1.46.0
AI model: n/a (deterministic round trip, no model call)
changed the title [-]Bug: An empty tool result becomes the string 'None' after ChatHistory.to_prompt() / from_rendered_prompt()[/-][+]Python: Bug: An empty tool result becomes the string 'None' after ChatHistory.to_prompt() / from_rendered_prompt()[/+]on Oct 29, 2026
I plan to implement this if the direction is right: normalize a missing <function_result> element text to "" in FunctionResultContent.from_element (or always emit a text node in to_element), plus regression coverage for the empty-result round trip and the Jinja messages helper. The reproduction above is deterministic and needs no model calls.
Could this issue be assigned to me (哈基米 (@BlueX888))? Happy to adjust the approach first if you'd prefer a different fix location.
Activity
[-]Bug: An empty tool result becomes the string 'None' after ChatHistory.to_prompt() / from_rendered_prompt()[/-][+]Python: Bug: An empty tool result becomes the string 'None' after ChatHistory.to_prompt() / from_rendered_prompt()[/+]BlueX888 commentedon Oct 29, 2026
I plan to implement this if the direction is right: normalize a missing
<function_result>element text to""inFunctionResultContent.from_element(or always emit a text node into_element), plus regression coverage for the empty-result round trip and the Jinjamessageshelper. The reproduction above is deterministic and needs no model calls.Could this issue be assigned to me (哈基米 (@BlueX888))? Happy to adjust the approach first if you'd prefer a different fix location.