Skip to content
Latchkey

Azure Pipelines "${{ }}" vs "$[ ]" vs "$( )" - Expression Mismatch

Azure has three expression syntaxes that evaluate at different times: ${{ }} (compile time, before the run), $[ ] (runtime, for variable values), and $( ) (macro, string interpolation at step start). Using the wrong one yields a literal, an empty value, or a compile error.

What this error means

A value comes out wrong: a ${{ }} that should reflect a runtime variable is frozen at its compile-time value (often empty), a $[ ] outside a variables: value does not evaluate, or a $( ) prints literally because the variable was not set when the step started.

azure-pipelines.yml
# wrong: compile-time read of a runtime variable
condition: ${{ eq(variables['Build.SourceBranch'], 'refs/heads/main') }}  # frozen, often false
# right:
condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')

Common causes

Compile-time syntax reading runtime state

${{ }} is expanded before the run, so it cannot see variables set during execution (output vars, most predefined run values). The result is the compile-time literal - usually empty or stale.

Runtime/macro syntax in the wrong slot

$[ ] is only valid as a variables: value; elsewhere it does not evaluate. $( ) only interpolates a variable that exists when the step begins - referencing one set later in the same step prints literally.

How to fix it

Pick the syntax by evaluation time

Use ${{ }} for parameters and compile-time logic, $[ ] for runtime variable values, and $( ) to interpolate into scripts.

azure-pipelines.yml
variables:
  isMain: $[ eq(variables['Build.SourceBranch'], 'refs/heads/main') ]   # runtime
steps:
  - script: echo "branch is $(Build.SourceBranch)"                       # macro
    condition: eq(variables.isMain, 'True')                              # runtime condition

Move runtime reads out of compile-time blocks

  1. Reserve ${{ }} for parameters.*, conditional insertion, and each loops.
  2. Compute runtime flags as $[ ] variables, then test them in conditions.
  3. Interpolate variables into shell with $( ), defining them before the step runs.

How to prevent it

  • Memorize the three syntaxes and their evaluation phase.
  • Keep parameter logic compile-time and run-result logic runtime.
  • Validate templates so misplaced expressions surface early.

Frequently asked questions

What causes ""${{ }}" vs "$[ ]" vs "$( )""?
${{ }} is expanded before the run, so it cannot see variables set during execution (output vars, most predefined run values). The result is the compile-time literal - usually empty or stale.
How do I fix "${{ }}" vs "$[ ]" vs "$( )"?
Use ${{ }} for parameters and compile-time logic, $[ ] for runtime variable values, and $( ) to interpolate into scripts.

Related guides

References

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