Description
Environment
agent_framework version: 1.2.2
- Python: 3.10
- Client:
OpenAIChatCompletionClient (Anthropic-compatible endpoint)
Description
When require_script_approval=True is set on SkillsProvider and the skill uses a
code-defined script (registered via the @skill.script decorator), the approval
callback fails with a 400 Bad Request:
openai.BadRequestError: Error code: 400
{"message":"Expected toolResult blocks at messages.0.content for the following Ids:
tooluse_xxx"}
Steps to Reproduce
from agent_framework import Skill, SkillsProvider
from agent_framework.openai import OpenAIChatCompletionClient
import asyncio, json
unit_converter_skill = Skill(
name="unit-converter",
description="Convert between common units using a conversion factor",
content="Use the convert script to perform unit conversions.",
)
@unit_converter_skill.script(name="convert", description="Convert a value: result =
value × factor")
def convert_units(value: float, factor: float) -> str:
result = round(value * factor, 4)
return json.dumps({"value": value, "factor": factor, "result": result})
skills_provider = SkillsProvider(
skill_paths=[],
skills=[unit_converter_skill],
require_script_approval=True,
instruction_template=(
"Skills:\n{skills}\n{runner_instructions}"
),
)
agent = OpenAIChatCompletionClient("model", base_url="...", api_key="...").as_agent(
name="Agent",
instructions="You are a helpful assistant.",
context_providers=[skills_provider],
)
async def main():
session = agent.create_session()
result = await agent.run("Convert 26.2 miles to km, factor=1.60934",
session=session)
# Approval loop
while result.user_input_requests:
approvals = [
r.to_function_approval_response(approved=True)
for r in result.user_input_requests
if r.function_call is not None
]
result = await agent.run(approvals, session=session) # ← 400 here
asyncio.run(main())
Expected Behavior
After sending the approval response, the framework executes the inline convert_units
function in-process and returns the result to the API as a proper function_result
block.
Actual Behavior
The second agent.run(approvals) call raises a 400 error: Expected toolResult blocks at messages.0.content.
Root Cause Analysis
After reading the source code in _tools.py, I identified two compounding defects:
Defect 1 — Wrong object type passed to executor (_process_function_requests,
~line 2066)
approved_responses contains function_approval_response objects, but they are passed directly to execute_function_calls as function_calls=. The executor expects
function_call objects. The fix should extract the embedded function_call from each
approval response:
# Current (buggy)
approved_responses = [resp for resp in fcc_todo.values() if resp.approved]
results = await execute_function_calls(function_calls=approved_responses, ...)
# Should be
function_calls_to_execute = [resp.function_call for resp in approved_responses]
results = await execute_function_calls(function_calls=function_calls_to_execute, ...)
Defect 2 — tool_map missing run_skill_script in the approval turn
(_auto_invoke_function, ~line 1468)
When agent.run(approvals) is called (a turn with no user text), the SkillsProvider
tools (run_skill_script, load_skill, etc.) are not re-registered in the tool_map. As a result, tool_map.get("run_skill_script") returns None, and the function falls through to the "hosted tool" branch, returning the original
function_approval_response object instead of a function_result.
This means approved_function_results in _replace_approval_contents_with_results
ends up containing function_approval_response objects, not function_result objects. The API then correctly rejects the message.
Workaround
Remove require_script_approval=True (and script_runner) from SkillsProvider.
Code-defined scripts are executed in-process without needing a runner, so approval
gating does not apply to them cleanly at present.
Notes
- File-based skills (with
skill.path set) were not tested; the bug is specific to
code-defined scripts registered via @skill.script.
- The framework documentation correctly states that code-defined scripts "are always
executed in-process and do not use a script runner," but require_script_approval
still intercepts them and triggers the broken approval flow.
Code Sample
Error Messages / Stack Traces
Package Versions
agent-framework-core 1.2.2
Python Version
3.10.18
Additional Context
No response
Description
Environment
agent_frameworkversion: 1.2.2OpenAIChatCompletionClient(Anthropic-compatible endpoint)Description
When
require_script_approval=Trueis set onSkillsProviderand the skill uses acode-defined script (registered via the
@skill.scriptdecorator), the approvalcallback fails with a 400 Bad Request:
Steps to Reproduce
Expected Behavior
After sending the approval response, the framework executes the inline
convert_unitsfunction in-process and returns the result to the API as a proper
function_resultblock.
Actual Behavior
The second
agent.run(approvals)call raises a 400 error:Expected toolResult blocks at messages.0.content.Root Cause Analysis
After reading the source code in
_tools.py, I identified two compounding defects:Defect 1 — Wrong object type passed to executor (
_process_function_requests,~line 2066)
approved_responsescontainsfunction_approval_responseobjects, but they are passed directly toexecute_function_callsasfunction_calls=. The executor expectsfunction_callobjects. The fix should extract the embeddedfunction_callfrom eachapproval response:
Defect 2 —
tool_mapmissingrun_skill_scriptin the approval turn(
_auto_invoke_function, ~line 1468)When
agent.run(approvals)is called (a turn with no user text), theSkillsProvidertools (
run_skill_script,load_skill, etc.) are not re-registered in thetool_map. As a result,tool_map.get("run_skill_script")returnsNone, and the function falls through to the "hosted tool" branch, returning the originalfunction_approval_responseobject instead of afunction_result.This means
approved_function_resultsin_replace_approval_contents_with_resultsends up containing
function_approval_responseobjects, notfunction_resultobjects. The API then correctly rejects the message.Workaround
Remove
require_script_approval=True(andscript_runner) fromSkillsProvider.Code-defined scripts are executed in-process without needing a runner, so approval
gating does not apply to them cleanly at present.
Notes
skill.pathset) were not tested; the bug is specific tocode-defined scripts registered via
@skill.script.executed in-process and do not use a script runner," but
require_script_approvalstill intercepts them and triggers the broken approval flow.
Code Sample
Error Messages / Stack Traces
Package Versions
agent-framework-core 1.2.2
Python Version
3.10.18
Additional Context
No response