Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

ShellTool: empty ShellResult(output=[]) fabricates stdout "(no output)" in the model-visible shell_call_output and persisted shell_output

Đang mở Phù hợp với người mới
#5,348 3 bình luận 0 reaction 0 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 1 ngày

Chưa có ai nhận issue này.

Đánh giá

Độ khó
2/5
Thời gian dự kiến
1-3 giờ
Mức phù hợp với người mới
77/100
Loại issue
Lỗi
Độ rõ ràng
Đặc tả rõ ràng
Mức độ hoạt động
Sôi nổi
Công nghệ
python
Lĩnh vực
tooling

Hướng nghiên cứu

Bắt đầu tại src/agents/run_internal/tool_actions.py, ở ShellAction.execute, nơi payload đã tuần tự hóa rỗng chuyển sang nhánh dự phòng dùng văn bản đã render. Đọc các nhánh liền kề của ShellResult và chuỗi thông thường, sau đó chạy tests/test_shell_tool.py cùng trường hợp tái hiện được cung cấp bằng ScriptedModel. Hoàn thành khi kết quả có cấu trúc rỗng tạo ra output: [] mà không có mục shell_output nào được lưu lại, đồng thời các trường hợp liền kề vẫn vượt qua.

Do mô hình lập chỉ mục viết ra từ nội dung của issue.

Mô tả

Describe the bug

When a ShellTool executor returns a structured ShellResult with an empty output list — i.e. ShellResult(output=[]), the legitimate result of a command that ran successfully and printed nothing (e.g. true, mkdir foo) — the SDK fabricates stdout that the executor never produced:

  • the model-visible shell_call_output payload for the next turn contains [{"stdout": "(no output)", "stderr": "", "outcome": {"type": "exit", "exit_code": 0}}]
  • the same fabricated entry is persisted into raw_item["shell_output"] with "outcome": "success"

"(no output)" is the human-readable placeholder that render_shell_outputs() returns for an empty output list, not command output. The model can quote it back to the user as real stdout, and any consumer parsing the structured shell_call_output entries (or the persisted shell_output) sees fake command output for a command that produced none.

Debug information
  • Agents SDK version: 0.23.1 (latest release; also reproduced on main @ 5f9899d584c5cfc879d3579352eb929fd4b34756, and the relevant code is unchanged on current main @ 125efa029b4bfd84238bd2c4fd69c3406f802663)
  • Python version: 3.13.15
  • Operating system: macOS 26.6
  • Model and model provider: reproduced with agents.testing.ScriptedModel (no network, no API keys) — the payload is built in the runner before any provider serialization, so this is provider-agnostic
  • Does the issue reproduce with the latest Agents SDK release? Yes, 0.23.1
  • Does the issue occur consistently or intermittently? Consistently
Repro steps

Minimal, self-contained, no network or API keys needed (agents.testing.ScriptedModel drives the run):

import asyncio
import json

from openai.types.responses import ResponseFunctionShellToolCall, ResponseOutputMessage, ResponseOutputText

from agents import Agent, Runner
from agents.testing import ScriptedModel
from agents.tool import ShellResult, ShellTool


def empty_output_executor(request):
    # Executor for a command that succeeded and printed nothing.
    return ShellResult(output=[])


def shell_call(call_id="call_shell"):
    return ResponseFunctionShellToolCall(
        id="sc1",
        call_id=call_id,
        type="shell_call",
        action={"type": "shell_command", "commands": ["true"]},
        status="completed",
    )


async def main():
    model = ScriptedModel(
        [
            [shell_call()],
            [
                ResponseOutputMessage(
                    id="m",
                    type="message",
                    role="assistant",
                    content=[
                        ResponseOutputText(text="done", type="output_text", annotations=[], logprobs=[])
                    ],
                    status="completed",
                )
            ],
        ]
    )
    agent = Agent(name="t", model=model, tools=[ShellTool(executor=empty_output_executor)])
    result = await Runner.run(agent, input="run true", max_turns=5)

    for item in result.new_items:
        if type(item).__name__ == "ToolCallOutputItem":
            print("MODEL-VISIBLE structured output:", json.dumps(item.raw_item["output"]))
            print("PERSISTED shell_output:", json.dumps(item.raw_item.get("shell_output")))
    print("final_output:", repr(result.final_output))


asyncio.run(main())
Actual output
MODEL-VISIBLE structured output: [{"stdout": "(no output)", "stderr": "", "outcome": {"type": "exit", "exit_code": 0}}]
PERSISTED shell_output: [{"stdout": "(no output)", "stderr": "", "status": "completed", "outcome": "success"}]
final_output: 'done'

The item the next model turn receives (ToolCallOutputItem.to_input_item()):

{"type": "shell_call_output", "call_id": "call_shell", "output": [{"stdout": "(no output)", "stderr": "", "outcome": {"type": "exit", "exit_code": 0}}]}
Expected behavior

The executor returned a structured result with no output entries, so the model-visible output should be [] and no shell_output entry should be persisted — the same faithful pass-through the sibling branches of this code path already produce:

  • ShellResult(output=[ShellCommandOutput(stdout="", stderr="")]) → model-visible {"output": [{"stdout": "", "stderr": "", "outcome": {"type": "exit", "exit_code": null}}]} — the empty stdout is passed through verbatim, nothing fabricated (verified by running the repro with this executor)
  • executor returning the plain string "" → model-visible {"output": []}, shell_output absent (verified the same way)

Three executor shapes that all mean "no output" are handled inconsistently inside one function, and only ShellResult(output=[]) gets a fabricated stdout.

Concrete basis:

  • render_shell_outputs (src/agents/run_internal/tool_execution.py:969-972) documents "(no output)" as its placeholder: "Render shell outputs into human-readable text for tool responses. / if not outputs: return "(no output)"". It is rendering text for the output string, not executor data, yet ShellAction.execute launders it into the structured channel as real stdout.
  • serialize_shell_output (src/agents/run_internal/tool_execution.py:936-937) is documented as "Serialize ShellCommandOutput for persistence or cross-run transmission" — the persisted shell_output entry in the bug case serializes nothing the executor produced.
  • ShellExecutor (src/agents/tool.py:1432-1433) is "Executes a shell command sequence and returns either text or structured output" — ShellResult(output=[]) is within the documented public contract, and serialize_shell_output over [] at src/agents/run_internal/tool_actions.py:578 already produces the faithful payload [].
  • The repo's own test test_shell_tool_structured_output_is_rendered (tests/test_shell_tool.py:226) asserts that raw_item["output"][0]["stdout"] mirrors the executor's stdout, and ToolCallOutputItem.to_input_item (src/agents/items.py:471-489) confirms raw_item["output"] is what the model receives.
Root cause

In ShellAction.execute (src/agents/run_internal/tool_actions.py:608), the truthiness check if shell_output_payload: treats the valid empty payload computed at tool_actions.py:578 as absent, so control falls into elif output_text: (tool_actions.py:610-618), which wraps the human-readable placeholder output_text (from render_shell_outputs, tool_execution.py:969-972) into a structured entry as if it were executor stdout, and that entry is persisted at tool_actions.py:632. The two branches do not distinguish "executor returned a structured ShellResult (possibly empty)" from "executor returned a plain string".

Happy to be assigned. For a fix direction: gate the elif output_text: fallback on whether the executor returned a structured ShellResult (e.g. a flag set in the isinstance(result, ShellResult) branch at tool_actions.py:562), so an empty-but-valid structured result yields output: [] exactly like the plain-empty-string sibling branch — I verified locally that this change makes the repro produce output: [] with no shell_output entry, leaves the two sibling cases unchanged, and passes all 22 tests in tests/test_shell_tool.py.

Related issues / PRs

I searched and found no existing report of this ("no output" shell, ShellResult, shell_output, ShellTool issues/PRs). The only open PR touching src/agents/run_internal/tool_actions.py is #5332 ("fix: preserve completed tool outputs when a sibling fails"), which is about sibling tool failures and unrelated to this fabrication.

Ngôn ngữ chính
Python
Star
30k
Fork
4.9k
Merge trung bình
15 giờ 43 phút
Pull request đã merge (30 ngày)
211

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của openai/openai-agents-python

Tất cả issue của openai/openai-agents-python

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.