Skip to content
Latchkey

Azure Pipelines "${{ each }}" Over a Non-Iterable Parameter

A compile-time ${{ each x in parameters.list }} loop needs the parameter to be a sequence or mapping (type: object). Iterating a string, or a parameter declared without type: object, fails to expand.

What this error means

The pipeline fails to compile when expanding an each loop, complaining the value is not iterable or the loop produces nothing. The template never expands the repeated block.

Azure DevOps
/templates/jobs.yml (Line: 6, Col: 9): Expected a sequence or mapping
for '${{ each region in parameters.regions }}'.

Common causes

Parameter is a string, not an object

A parameter declared without type: object defaults to string. each over a string is invalid - declare it as type: object and pass a YAML list/map.

Wrong loop-variable reference

Inside the loop, reference the iterator as ${{ region }} (or ${{ region.key }} for maps). Using $(region) (a macro) or the wrong property fails or yields nothing.

How to fix it

Declare the parameter as an object and pass a list

Use type: object so the value can be iterated.

templates/jobs.yml
# template
parameters:
  - name: regions
    type: object
    default: []
jobs:
  - ${{ each region in parameters.regions }}:
    - job: deploy_${{ region }}
      steps: [ { script: echo ${{ region }} } ]

Pass the list from the caller

azure-pipelines.yml
extends:
  template: templates/jobs.yml
  parameters:
    regions: [ eastus, westus ]

How to prevent it

  • Declare iterated parameters as type: object.
  • Reference loop variables with ${{ }}, not the $( ) macro.
  • Validate the template after changing a parameter’s type.

Frequently asked questions

What causes ""${{ each }}" fails"?
A parameter declared without type: object defaults to string. each over a string is invalid - declare it as type: object and pass a YAML list/map.
How do I fix "${{ each }}" fails?
Use type: object so the value can be iterated.

Related guides

References

Latchkey auto-heals failures like this one - detected, fixed, and retried without you. Start free → 30-day trial · No credit card