Python: [Bug]: Local shell results are sent to OpenAI as exit 0, even when the command failed or timed out
I maintainer di solito rispondono entro 1 giorno
@eavanvalkenburg ci sta già lavorando.
Dal 1/10/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
Description
When a local shell tool runs through the OpenAI Responses client, its result goes back as a shell_call_output whose structured outcome is always {"type": "exit", "exit_code": 0}. That holds when the command exited non-zero and when it timed out. stderr is also sent inside stdout, and the stderr field is empty.
The cause is a mismatch between two packages:
LocalShellTool.as_function()returnsShellResult.format_for_model(), a single string such as"stderr: oops\n\nexit_code: 1"(agent_framework_tools/shell/_tool.py:314;DockerShellTooldoes the same at_docker.py:805).RawOpenAIChatClient._to_shell_call_output_payload(agent_framework_openai/_chat_client.py:2391) can buildstdout,stderr,exit_codeandtimed_outfrom a mapping. Given a string, it puts the whole text instdoutand falls back toexit_code = 0, because there is no exception.
So the model receives a structured outcome that contradicts the text next to it: a failed command reported as success, and a timeout reported as a clean exit rather than the payload's dedicated {"type": "timeout"}.
Expected: outcome matches the command's real result (exit_code: 3, exit_code: 1, {"type": "timeout"}), and stderr goes in stderr.
Actual: outcome is {"type": "exit", "exit_code": 0} in all three cases below.
Code Sample
import asyncio
import tempfile
from agent_framework import Content
from agent_framework.openai import OpenAIChatClient
from agent_framework_tools.shell import LocalShellTool
async def main() -> None:
shell = LocalShellTool(mode="stateless", workdir=tempfile.mkdtemp(), approval_mode="never_require",
acknowledge_unsafe=True, timeout=2)
shell_tool = OpenAIChatClient.get_shell_tool(func=shell.as_function())
# PowerShell syntax (Windows); on Linux/macOS use e.g. "echo oops >&2; exit 1" and "sleep 5".
for command in ("exit 3", "[Console]::Error.WriteLine('oops'); exit 1", "Start-Sleep 5"):
result = await shell_tool.invoke(arguments={"command": command}) # what the function loop receives
payload = OpenAIChatClient._to_shell_call_output_payload(Content.from_function_result(call_id="c", result=result))
print(f"$ {command}\n tool text : {result[0].text!r}\n sent : {payload}")
asyncio.run(main())
Error Messages / Stack Traces
$ exit 3
tool text : 'exit_code: 3'
sent : [{'stdout': 'exit_code: 3', 'stderr': '', 'outcome': {'type': 'exit', 'exit_code': 0}}]
$ [Console]::Error.WriteLine('oops'); exit 1
tool text : 'stderr: oops\r\n\nexit_code: 1'
sent : [{'stdout': 'stderr: oops\r\n\nexit_code: 1', 'stderr': '', 'outcome': {'type': 'exit', 'exit_code': 0}}]
$ Start-Sleep 5
tool text : '[command timed out]\nexit_code: 15'
sent : [{'stdout': '[command timed out]\nexit_code: 15', 'stderr': '', 'outcome': {'type': 'exit', 'exit_code': 0}}]
Package Versions
agent-framework-core: 1.19.0, agent-framework-openai: 1.14.4, agent-framework-tools: 1.0.0b260918
Python Version
Python 3.13.13 (Windows 11, PowerShell)
Additional Context
- Reproduced on the released packages above, in a fresh environment, and on
mainat7634356a1. - Only the OpenAI shell path is affected. With Anthropic's
bash_*tool the result goes back as atool_resultcarrying the text (withis_errorset only on an exception), and a plain function tool sends the text as its output, so neither has a structured exit code to contradict it. - Proposed fix: have the shell tools return their structured result alongside the text, and have the OpenAI payload builder use it. Core already defines
Content.from_shell_command_output(stdout=, stderr=, exit_code=, timed_out=), which the OpenAI and Anthropic clients use today for inbound hosted shell output. The local tools could emit the same content, and_to_shell_call_output_payloadcould buildstdout,stderrand theexit/timeoutoutcome from it. Providers that take text keep receivingformat_for_model()'s output, so nothing changes for them. Reading the exit code back out of the text would be a smaller change, but it would tie the payload to a display format. - This crosses
agent-framework-toolsandagent-framework-openai, and changes how shell results are serialized, whichdocs/specs/004-python-function-calling-loop.mdcovers, so I'd like a maintainer's OK on this direction first. I'm happy to take it.
- Lingua principale
- Python
- Stelle
- 14k
- Fork
- 2.5k
- Merge medio
- 1g 20h
- PR unite (30g)
- 370
Preparare l'ambiente
Avvia il container di sviluppo del progetto nel browser, con il tuo account GitHub.
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di microsoft/agent-framework
-
.NET python triage
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 85/100
microsoft/agent-framework#9092 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Python: raw-data content mappings lose annotations and attachment metadataForse già presa @moonbox3 l’ha presa 12 giorni fa. Apertapython triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
microsoft/agent-framework#8632 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
-
Python: Clarify when to use platformForse già presa @eavanvalkenburg l’ha presa 12 giorni fa. Apertapython triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
microsoft/agent-framework#8599 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
.NET Compaction - Update docs to refer to `AIContextProvider` deep diveForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta.NET compaction documentation
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 82/100
microsoft/agent-framework#4629 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Python: [Bug]: After a checkpoint restore, a sub-workflow re-emits a pending request while handling a sibling response, so later restores keep asking for an already-answered requestForse già presa @eavanvalkenburg l’ha presa oggi. Apertapython reproduced workflows
microsoft/agent-framework#9270 · 2 commenti · 1 assegnatario ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di microsoft/agent-framework
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
KalicoCrew/kalico#1012 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 82/100
esphome/esphome.io#7657 ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 62/100
VNG-Realisatie/gemma-zaken#2658 ·
-
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno
-
comp/gateway comp/plugins duplicate P3 type/bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 82/100
NousResearch/hermes-agent#137019 ·
I maintainer di solito rispondono entro 1 giorno