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.
# 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.
variables:
isMain: $[ eq(variables['Build.SourceBranch'], 'refs/heads/main') ] # runtime
steps:
- script: echo "branch is $(Build.SourceBranch)" # macro
condition: eq(variables.isMain, 'True') # runtime conditionMove runtime reads out of compile-time blocks
- Reserve
${{ }}forparameters.*, conditional insertion, andeachloops. - Compute runtime flags as
$[ ]variables, then test them in conditions. - 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.