Skip to content

Python: [Bug]: Cannot approve tool usage from sub-agents #4963

Closed

Description

Description

When a sub-agent is used via .as_tool() and its inner tool requires approval_mode="always_require", the approval request correctly propagates to the caller. However, sending the approval response back and resuming does not work. The inner tool is never executed.

Code Sample

async def nonstreaming():
    chat_client = SomeChatClient()

    @tool(approval_mode="always_require")
    def get_weather_detail(location: Annotated[str, "The city and state, e.g. San Francisco, CA"]) -> str:
        answer = f"The weather in {location} is cloudy with a high of 15°C, humidity 88%."
        return answer

    # Create weather assistant as inner agent
    inner_agent = chat_client.as_agent(
        instructions="You are a helpful assistant that provides weather information to users.",
        description="Agent that provides weather information.",
        name="weather_agent",
        tools=[get_weather_detail],
    )

    # Create main Agent that uses inner agent as tool
    outer_agent = chat_client.as_agent(
        instructions=(
            "You are a helpful coordinator. When a user asks about weather, "
            "use the weather_agent_tool to get the information."
        ),
        description="Coordinator agent that delegates to the weather agent tool.",
        name="coordinator_agent",
        tools=[
            inner_agent.as_tool(
                name="weather_agent_tool",
                description="A weather agent tool that provides current weather information for any given location.",
                approval_mode='never_require'
            )
        ],
    )

    user_input = "What's the weather like in Amsterdam?"
    session = outer_agent.create_session()

    response = await outer_agent.run(user_input, session=session)
    new_inputs: list[Message] = []
    if response.user_input_requests:
        for req in response.user_input_requests:
            print(f'User input requested - Func name: {req.function_call.name}, Args: {req.function_call.arguments}')
            new_inputs.append(Message("user", [req.to_function_approval_response(True)]))

    print("\nRunning agent with new inputs after approvals...\n")
    response2 = await outer_agent.run(new_inputs, session=session)
    print(f"Final response: {response2}")

Error Messages / Stack Traces

Result:

User input requested - Func name: get_weather_detail, Args: {"location":"Amsterdam, Netherlands"}

Running agent with new inputs after approvals...

Final response: I'm fetching the current weather in Amsterdam for you. One moment!


Here is the output when the tool approval model is set to `never_require`

Running agent with new inputs after approvals...

Final response: The current weather in Amsterdam is cloudy, with a high of 15°C and a humidity level of 88%.

Package Versions

agent-framework-core: 1.0.0rc5

Python Version

Python 3.12.12

Additional Context

  • The bug occurred in both streaming and non-streaming mode.
  • Tested on Workflow.as_tool(), doesn't work either.

Activity

  1. added
    bugUsage: [Issues], Target: all issues (Legacy, prefer issue type: bug)
    on Mar 29, 2026
  2. removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Mar 30, 2026
  3. moved this from Planned to In Progress in Agent Frameworkon Apr 2, 2026
  4. moved this from In Progress to Planned in Agent Frameworkon May 4, 2026
  5. SuperMarimoBros commented on Sep 11, 2026

    @SuperMarimoBros

    I can reproduce this on the current Python packages with a minimal nested-Skill variant.

    Setup:

    • Outer Agent exposes a child Agent through child.as_tool(..., approval_mode="always_require").
    • The child has a SkillsProvider with an InlineSkill.
    • The Skill tools retain their default approval requirement.
    • The child requests load_skill; its approval request is visible through the child stream callback, but it is not surfaced in outer_result.user_input_requests.

    The outer tool approval is returned and can be approved. The child starts, then its load_skill approval cannot be answered by the caller. The child run stops with Tool requires user input to proceed.

    I observed the same primary failure with both clients:

    Client Follow-on result after the nested approval failure
    OpenAIChatCompletionClient HTTP 400: the outer note_worker tool call has no matching tool response
    OpenAIChatClient ChatClientInvalidRequestException: Messages are required for chat completions

    Versions:

    • agent-framework==1.18.0
    • agent-framework-openai==1.14.3
    • openai==2.54.0
    • Python 3.13.5

    This appears to be the same approval-routing/resume gap as this issue, now reproduced through the built-in Skills path as well. The missing user-visible approval is the child Skill tool (load_skill), while the Chat Completions 400 is a subsequent missing outer tool-result pair.

  6. eavanvalkenburg commented on Sep 18, 2026

    @eavanvalkenburg
    Member

    We have chosen a deliberately smaller contract for this scenario, implemented in #8527.

    Agent.as_tool() is intended for delegated calls that can complete within the current invocation. When a child tool requires approval:

    • the child can decide immediately through its own ToolApprovalMiddleware(auto_approval_rules=[...]);
    • those rules are evaluated against the runtime function call, so they also cover tools added after agent construction by integrations such as MCP or Skills;
    • if no child policy approves the call, the delegated invocation fails closed with guidance instead of returning an approval that the parent cannot safely resume;
    • unrelated user-input requests retain their existing behavior.

    Approvals involving a person, a long wait, persistence, external coordination, cancellation, or process restarts should be modeled as a Workflow pause and resume. The PR includes a sample showing the same scenario first with an immediate Agent.as_tool() policy and then with durable Workflow approval.

    We considered transparent propagation through the caller, application callbacks, and resumable child continuations. Those approaches require a broader contract for ownership, persistence, provider transcripts, cancellation, and nested or parallel calls. They are not necessary to fix this bug safely.

    #8527 contains Fixes #4963, so this issue will close automatically when the PR merges. If there is demand for transparent or resumable nested-agent approvals beyond this contract, please open a separate feature issue describing that use case and its required lifetime semantics rather than extending this bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

agentsUsage: [Issues, PRs], Target: Single agentbugUsage: [Issues], Target: all issues (Legacy, prefer issue type: bug)pythonUsage: [Issues, PRs], Target: Python

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions