Description
When an InvokeAzureAgent action returns a JSON object containing an array of objects, and the output is captured via responseObject, all nested object properties are silently dropped. Each object in the array becomes {}. Subsequent Foreach iteration over the array crashes with an unhandled workflow failure.
This affects any schema-less JSON parsing path in declarative workflows, not only InvokeAzureAgent.
To Reproduce
1. Workflow YAML
kind: Workflow
name: repro-empty-records
trigger:
kind: OnConversationStart
id: trigger
actions:
- kind: InvokeAzureAgent
id: invoke_agent
agent:
name: SomeAgent
conversationId: =System.ConversationId
input:
messages: =[UserMessage("list team members")]
output:
responseObject: Local.agent_output
- kind: SendActivity
id: show_items
activity: "{Local.agent_output.items}"
- kind: Foreach
id: iterate_items
items: =Local.agent_output.items
value: Local.currentItem
index: Local.index
actions:
- kind: SendActivity
id: show_item
activity: "{Local.currentItem}"
- kind: EndWorkflow
id: end
2. Agent JSON response (last message text)
{
"items": [
{ "name": "Alice", "role": "Engineer" },
{ "name": "Bob", "role": "Designer" },
{ "name": "Carol", "role": "PM" }
]
}
3. Expected behavior
SendActivity at show_items should display the full array with object properties intact. Foreach should iterate and Local.currentItem should contain each record with name and role fields.
4. Actual behavior
SendActivity outputs: [ {}, {}, {} ] — all object properties are lost.
Foreach crashes: Unhandled workflow failure - #iterate_items (Foreach) — because ForeachExecutor calls .Properties.Values.First() on records that have zero properties.
Root Cause
The bug is in JsonDocumentExtensions.cs, inside the ParseTable method's local function DetermineElementType():
// Line ~114
JsonValueKind.Object => VariableType.Record(targetType.Schema?.Select(kvp => (kvp.Key, kvp.Value)) ?? []),
When targetType.Schema is null (no schema defined — always the case for responseObject since it is parsed with VariableType.RecordType), the ?? [] fallback passes an empty collection to VariableType.Record(). This creates a VariableType with Schema set to an empty FrozenDictionary (non-null, count = 0).
Downstream, ParseRecord checks targetType.Schema is null to decide between dynamic parsing (ParseValues()) and schema-based parsing (ParseSchema()):
// Lines ~47-50
IEnumerable<KeyValuePair<string, object?>> keyValuePairs =
targetType.Schema is null ?
ParseValues() : // <-- dynamic: preserves all JSON properties
ParseSchema(targetType.Schema); // <-- schema-based: iterates schema fields only
Because Schema is an empty dictionary (not null), it takes the ParseSchema path, iterates over zero schema fields, and returns an empty dictionary for every object in the array. All JSON properties are silently discarded.
Note that VariableType.HasSchema correctly handles this ((this.Schema?.Count ?? 0) > 0 returns false for empty schemas), but ParseRecord uses a null check instead of HasSchema.
Cascade to ForeachExecutor
ForeachExecutor.ExecuteAsync extracts values via:
this._values = [.. tableValue.Values.Select(value => value.Properties.Values.First().ToFormula())];
Since each RecordDataValue has empty Properties, .First() throws, producing the unhandled workflow failure.
Proposed Fix
In JsonDocumentExtensions.cs, DetermineElementType(), change the JsonValueKind.Object case to avoid creating a schema-bound VariableType when there is no schema to propagate:
- JsonValueKind.Object => VariableType.Record(targetType.Schema?.Select(kvp => (kvp.Key, kvp.Value)) ?? []),
+ JsonValueKind.Object => targetType.HasSchema
+ ? VariableType.Record(targetType.Schema!.Select(kvp => (kvp.Key, kvp.Value)))
+ : VariableType.RecordType,
When HasSchema is false, this uses VariableType.RecordType (typeof(IDictionary<string, object?>) with Schema = null), so ParseRecord takes the dynamic ParseValues() path and preserves all JSON properties.
Related
Code Sample
Error Messages / Stack Traces
Package Versions
Microsoft.Agents.AI.Abstractions:1.0.0-preview.260212.1, Microsoft.Agents.AI.Declarative:1.0.0-preview.260212.1, Microsoft.Agents.AI.OpenAI:1.0.0-preview.260212.1, Microsoft.Agents.AI.Workflows:1.0.0-preview.260212.1, Microsoft.Agents.AI.Workflows.Declarative:1.0.0-preview.260212.1
.NET Version
.NET 8.0
Additional Context
No response
Description
When an
InvokeAzureAgentaction returns a JSON object containing an array of objects, and the output is captured viaresponseObject, all nested object properties are silently dropped. Each object in the array becomes{}. SubsequentForeachiteration over the array crashes with an unhandled workflow failure.This affects any schema-less JSON parsing path in declarative workflows, not only
InvokeAzureAgent.To Reproduce
1. Workflow YAML
2. Agent JSON response (last message text)
{ "items": [ { "name": "Alice", "role": "Engineer" }, { "name": "Bob", "role": "Designer" }, { "name": "Carol", "role": "PM" } ] }3. Expected behavior
SendActivityatshow_itemsshould display the full array with object properties intact.Foreachshould iterate andLocal.currentItemshould contain each record withnameandrolefields.4. Actual behavior
SendActivityoutputs:[ {}, {}, {} ]— all object properties are lost.Foreachcrashes:Unhandled workflow failure - #iterate_items (Foreach)— becauseForeachExecutorcalls.Properties.Values.First()on records that have zero properties.Root Cause
The bug is in
JsonDocumentExtensions.cs, inside theParseTablemethod's local functionDetermineElementType():When
targetType.Schemaisnull(no schema defined — always the case forresponseObjectsince it is parsed withVariableType.RecordType), the?? []fallback passes an empty collection toVariableType.Record(). This creates aVariableTypewithSchemaset to an emptyFrozenDictionary(non-null, count = 0).Downstream,
ParseRecordcheckstargetType.Schema is nullto decide between dynamic parsing (ParseValues()) and schema-based parsing (ParseSchema()):Because
Schemais an empty dictionary (notnull), it takes theParseSchemapath, iterates over zero schema fields, and returns an empty dictionary for every object in the array. All JSON properties are silently discarded.Note that
VariableType.HasSchemacorrectly handles this ((this.Schema?.Count ?? 0) > 0returnsfalsefor empty schemas), butParseRecorduses a null check instead ofHasSchema.Cascade to
ForeachExecutorForeachExecutor.ExecuteAsyncextracts values via:Since each
RecordDataValuehas emptyProperties,.First()throws, producing the unhandled workflow failure.Proposed Fix
In
JsonDocumentExtensions.cs,DetermineElementType(), change theJsonValueKind.Objectcase to avoid creating a schema-boundVariableTypewhen there is no schema to propagate:When
HasSchemaisfalse, this usesVariableType.RecordType(typeof(IDictionary<string, object?>)withSchema = null), soParseRecordtakes the dynamicParseValues()path and preserves all JSON properties.Related
Code Sample
Error Messages / Stack Traces
Package Versions
Microsoft.Agents.AI.Abstractions:1.0.0-preview.260212.1, Microsoft.Agents.AI.Declarative:1.0.0-preview.260212.1, Microsoft.Agents.AI.OpenAI:1.0.0-preview.260212.1, Microsoft.Agents.AI.Workflows:1.0.0-preview.260212.1, Microsoft.Agents.AI.Workflows.Declarative:1.0.0-preview.260212.1
.NET Version
.NET 8.0
Additional Context
No response