Skip to content

Python: always_require tool approval silently bypassed when preceded by a declaration-only or unknown call in the same batch #8079

Description

@CorgiBoyG

Description

In the Python core, a tool configured with approval_mode="always_require" can be silently bypassed (executed or returned as user input without surfacing an approval request) when another call precedes it in the same model tool-call batch. The approval outcome depends on the position of the call within the batch rather than on the call itself.

Root cause

_try_execute_function_call_groups in python/packages/core/agent_framework/_tools.py classifies the batch with a loop that stops at the first matching call:

for function_call in actionable_calls:
    function_name = function_call.name
    if function_name in approval_tool_names:
        requires_approval = True
        break
    if function_name in declaration_only_tool_names or function_name in additional_tool_names:
        has_declaration_only_call = True
        break
    if config.get("terminate_on_unknown_calls", False) and function_name not in tool_map:
        raise KeyError(f'Error: Requested function "{function_name}" not found.')

Because each branch breaks (or raises) at the first hit, classification travels with the call's position, not with the call:

  • A declaration-only call appearing before an always_require call makes the whole batch classified as declaration-only user input, so the approval gate is never surfaced.
  • With terminate_on_unknown_calls=True, an unknown call appearing before an always_require call raises KeyError first, so the higher-priority approval pause is never reached.

Reproduction

Model returns one batch with two calls where a declaration-only (or unknown) call is ordered first and an always_require tool is second. Expected: an approval request is surfaced for the always_require tool. Actual: no approval request; the batch is returned as user input (declaration-first) or terminated with KeyError (unknown-first).

Expected invariant

Approval enforcement must be independent of call order within a batch. Classification should scan the whole batch and apply a fixed priority: approval pause > declaration-only user-input > unknown-call termination.

Scope

python/packages/core/agent_framework/_tools.py batch classification in _try_execute_function_call_groups. Verified present on current main.

Note: this is distinct from #6385 (which concerned the opposite direction, never_require tools being over-approved) and from #7881 (a concurrency-groups feature that retains the current order-dependent classification).

I'm working on a fix with regression tests and will open a PR shortly with Fixes #NNNN.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    pythonUsage: [Issues, PRs], Target: PythontriageUsage: [Issues], Target: All issues that still need to be triaged

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions