microsoft / microsoft/agent-framework
.NET: Python: [Bug]: InvokeAzureAgent silently drops `input.arguments`, never sends them to the agent
@peibekwe is already working on this.
Since Aug 27, 2026.
- Dominant language
- Python
- Stars
- 13.6k
- Forks
- 2.3k
- Avg merge
- 2d 45m
- Merged PRs (30d)
- 358
Description
Description
InvokeAzureAgentExecutor (the executor backing the declarative
InvokeAzureAgent action) accepts a .NET-style input.arguments mapping,
as documented in its own class docstring and used by the repo's own
customer_support sample (ticket_agent, escalate_agent, resolution_agent
actions in
python/samples/getting_started/workflows/declarative/customer_support/workflow.yaml,
which invoke TicketingAgent / TicketEscalationAgent / TicketResolutionAgent
with only input.arguments, no input.messages).
In _build_input_text()
(agent_framework_declarative/_workflows/_executors_agents.py), every entry
of input.arguments is evaluated into a local evaluated_args dict — and
then never used again:
async def _build_input_text(self, state, arguments, messages_expr) -> str:
# Evaluate arguments
evaluated_args: dict[str, Any] = {}
for key, value in arguments.items():
evaluated_args[key] = state.eval_if_expression(value)
# Evaluate messages/input
if messages_expr:
...
# evaluated_args is not read from this point on
...
return input_text if input_text else ""
evaluated_args is dropped: it isn't concatenated into the returned text,
and isn't stored anywhere in workflow state. Note that options/run_kwargs
is not a viable channel for it either — that dict is populated from the
kwargs passed to workflow.run() (via WORKFLOW_RUN_KWARGS_KEY) so that
tools can see them; it has nothing to do with input.arguments, and
SupportsAgentRun.run() has no separate "arguments" parameter at all, only
messages. So messages and arguments are two distinct inputs on the
declarative-action side (_build_input_text(state, arguments, messages_expr)),
but only one channel — the returned text, which becomes the message sent to
agent.run() — actually reaches the agent. The fix has to fold
evaluated_args into that returned text (the way the existing
Workflow.Inputs fallback already formats multiple values as key: value
lines), not thread them through options. As a result, any
InvokeAzureAgent action that relies on input.arguments without also
specifying input.messages sends the agent no information about those
arguments at all — in the reproduction below, the agent is invoked with an
empty string.
The equivalent .NET implementation
(InvokeAzureAgentExecutor.cs, InvokeAgentAsync, lines 63-64) evaluates the
structured inputs via GetStructuredInputs() into inputParameters and
threads them into agentProvider.InvokeAgentAsync(...), so this is a
Python-only regression relative to .NET feature parity, not an intentional
behavior difference.
The same "evaluate into evaluated_args, then never use it" pattern also
exists in the older interpreter-based action handler,
_actions_agents.py::_run_invoke_azure_agent (not currently wired into
WorkflowFactory, but shipped in the package) — so this isn't isolated to
one code path.
Expected behavior: the evaluated input.arguments values are included in
(or otherwise made available to) the input sent to the agent, matching the
documented schema and the .NET behavior.
Actual behavior: input.arguments are evaluated and silently discarded;
the agent receives none of that data.
Steps to reproduce:
- Define a declarative workflow with an
InvokeAzureAgentaction that uses
input.argumentsand noinput.messages(see the workflow definition
below). - Run the workflow with
WorkflowFactory, using an agent that records what
it's asked to run. - Observe that the agent receives an empty string instead of the arguments.
A minimal, self-contained repro (using a stub agent, no Azure credentials
required) is included below.
Code Sample
Workflow definition:
kind: Workflow
trigger:
kind: OnConversationStart
id: main
actions:
- kind: InvokeAzureAgent
id: ticket_agent
agent:
name: TicketingAgent
input:
arguments:
IssueDescription: "The printer on the 3rd floor is jammed."
AttemptedResolutionSteps: "Restarted the printer twice."
output:
resultProperty: Local.AgentReply
Reproduction script:
import asyncio
from pathlib import Path
from typing import Any
from agent_framework.declarative import WorkflowFactory
class RecordingAgent:
"""Duck-typed stand-in for a real agent (see `SupportsAgentRun` protocol).
Just records what it was asked to run instead of calling a model.
"""
def __init__(self, name: str) -> None:
self.id = f"{name}-001"
self.name = name
self.description = "Records the input it receives; does not call a model."
self.received_input: Any = "<agent.run() was never called>"
async def run(self, received_input: Any = None, *, options: Any = None, **kwargs: Any) -> Any:
self.received_input = received_input
class _Result:
text = "(stub response)"
return _Result()
def get_new_thread(self, **kwargs: Any) -> dict[str, Any]:
return {"id": "stub-thread", "messages": []}
async def main() -> None:
agent = RecordingAgent("TicketingAgent")
factory = WorkflowFactory(agents={"TicketingAgent": agent})
workflow = factory.create_workflow_from_yaml_path(Path(__file__).parent / "workflow.yaml")
async for _event in workflow.run({}, stream=True):
pass
defined_arguments = {
"IssueDescription": "The printer on the 3rd floor is jammed.",
"AttemptedResolutionSteps": "Restarted the printer twice.",
}
agent_was_invoked = agent.received_input != "<agent.run() was never called>"
received_text = str(agent.received_input).lower()
missing_arguments = [
name for name, value in defined_arguments.items() if value.lower() not in received_text
]
print("input.arguments defined in workflow.yaml:")
print(f" {defined_arguments}")
print()
print("What TicketingAgent.run() actually received:")
print(f" {agent.received_input!r}")
print()
if not agent_was_invoked:
print("BUG: TicketingAgent.run() was never even called.")
elif missing_arguments:
print(
"BUG CONFIRMED: TicketingAgent.run() WAS called (the workflow -> agent "
"wiring works), but what it received does not include the values from "
f"input.arguments: {missing_arguments}. _build_input_text() evaluates "
"them into `evaluated_args` and then never reads that variable again, "
"so the arguments are silently dropped before the agent is invoked."
)
else:
print("OK: input.arguments made it to the agent. The bug appears fixed.")
if __name__ == "__main__":
asyncio.run(main())
Error Messages / Stack Traces
None — this is a silent data-loss bug, not an exception. Running the script
above prints:
input.arguments defined in workflow.yaml:
{'IssueDescription': 'The printer on the 3rd floor is jammed.', 'AttemptedResolutionSteps': 'Restarted the printer twice.'}
What TicketingAgent.run() actually received:
''
BUG CONFIRMED: TicketingAgent.run() WAS called (the workflow -> agent wiring works), but what it received does not include the values from input.arguments: ['IssueDescription', 'AttemptedResolutionSteps']. _build_input_text() evaluates them into `evaluated_args` and then never reads that variable again, so the arguments are silently dropped before the agent is invoked.
i.e. `TicketingAgent.run()` is called (the workflow → agent wiring works),
but what it receives is `''` instead of something that reflects
`IssueDescription` / `AttemptedResolutionSteps`.
Package Versions
agent-framework-declarative: 1.0.3, agent-framework: 1.15.0, agent-framework-core: 1.15.0 (also reproduces on agent-framework-declarative 1.0.0rc2 / agent-framework 1.11.0 / agent-framework-core 1.11.0, and on microsoft/agent-framework's main branch as of 2026-08-19)
Python Version
Python 3.13.1
Additional Context
Workaround: pass the same values via input.messages instead of
input.arguments (e.g. compose a single message string yourself), or set
Local.input / Local.userInput directly before the action runs.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.