Thanks to visit codestin.com
Credit goes to github.com

Skip to content

.NET: [Bug]: Declarative Foreach collapses multi-field records to their first field in the loop value variable #6183

Description

@yurii-beketov

Description

In declarative workflows, when a Foreach action iterates over a table whose rows are multi-field records, the loop value variable receives only the first field's value of each record instead of the full record.

For example, iterating over [{ name: "Alice", role: "Engineer" }, ...] assigns "Alice" to Local.currentItem rather than the whole { name, role } record, so Local.currentItem.role is unavailable inside the loop body.

This is distinct from #4195 (parsing produced empty records). Here the records are correctly populated, but ForeachExecutor deliberately reduces each one to its first property.

To Reproduce

1. Workflow YAML

kind: Workflow
name: repro-foreach-first-field
trigger:
  kind: OnConversationStart
  id: trigger
  actions:
    - kind: SetVariable
      id: set_people
      variable: Local.people
      value: |
        =Table(
          { name: "Alice", role: "Engineer" },
          { name: "Bob",   role: "Designer" },
          { name: "Carol", role: "PM" }
        )
    - kind: Foreach
      id: iterate_people
      items: =Local.people
      value: Local.currentItem
      index: Local.index
      actions:
        - kind: SendActivity
          id: show_item
          activity: "item={Local.currentItem} role={Local.currentItem.role}"
    - kind: EndWorkflow
      id: end

2. Expected behavior

Local.currentItem should be the full record for each iteration, so show_item prints e.g. item={ name: "Alice", role: "Engineer" } role=Engineer.

3. Actual behavior

Local.currentItem is just the first field's value ("Alice"), and Local.currentItem.role resolves to blank. The whole record is lost.

Root Cause

ForeachExecutor.ExecuteAsync collapses each row to its first property:

// ForeachExecutor.cs, line 47
this._values = [.. tableValue.Values.Select(value => value.Properties.Values.First().ToFormula())];

tableValue.Values is a sequence of RecordDataValue. Calling .Properties.Values.First() keeps only the first field's value and discards the rest of the record.

The framework already has the correct conversion elsewhere — DataValueExtensions.ToFormula maps each row through value.ToRecordValue(), preserving all fields:

TableDataValue tableValue =>
    FormulaValue.NewTable(
        tableValue.Values.FirstOrDefault()?.ParseRecordType() ?? RecordType.Empty(),
        tableValue.Values.Select(value => value.ToRecordValue())),
...
RecordDataValue recordValue => recordValue.ToRecordValue(),

ForeachExecutor simply does not use that path.

Proposed Fix

In ForeachExecutor.ExecuteAsync, convert each row as a whole value so records keep every field:

- this._values = [.. tableValue.Values.Select(value => value.Properties.Values.First().ToFormula())];
+ this._values = [.. tableValue.Values.Select(value => value.ToFormula())];

Each RecordDataValue then routes through the RecordDataValue recordValue => recordValue.ToRecordValue() branch of ToFormula, producing a full RecordValue. This makes Local.currentItem.<field> work directly and removes the need for the common user-side workaround of declaring an index and using Index(originalCollection, Local.index + 1).FieldName.

Tests Gap

The existing tests in ForeachExecutorTest.cs only build records with a single field (e.g. value/item), so .First() happens to return the correct value and the bug stays hidden. A test that iterates over multi-field records and asserts value is the full record would catch this regression.

Related

Package Versions

Reproduced against origin/main (ForeachExecutor.cs line 47 unchanged as of filing; file last modified 2026-02-12, #3835).

.NET Version

.NET 8.0

Metadata

Metadata

Assignees

Labels

.NETUsage: [Issues, PRs], Target: .NetbugUsage: [Issues], Target: all issues (Legacy, prefer issue type: bug)declarativeUsage: [Issues, PRs], Target: declarative agents and workflowsreproducedUsage: [Issues], Target: all issues that can be reproduced by the triage workflow

Type

Fields

No fields configured for Bug.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions