GitLab CI Variable Expansion Errors - Empty or Wrong Values in Rules
A $VARIABLE resolved to empty or to the wrong value. GitLab expands variables in defined phases, and a value not yet set at rule-evaluation time, or an over-escaped $$, breaks the logic.
What this error means
A rule never matches because the variable it tests is empty, a script prints a literal $VAR instead of its value, or a nested variable reference does not resolve. The config is valid - the value is just not what you expect.
# Rule never matches because the variable is set in the same job, not at rule time:
deploy:
variables:
TARGET: production
rules:
- if: '$TARGET == "production"' # $TARGET evaluated before job vars applyDiagnose it: which rule matched, and on which runner?
GitLab evaluates rules: top to bottom and the first match wins, including one that sets when: never. A job that does not run, or runs when you did not expect it to, is nearly always matching an earlier rule than the one you are reading.
# validate the definition against the project
curl -s --header "PRIVATE-TOKEN: $TOKEN" \
"https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/ci/lint" \
--data-urlencode "content=$(cat .gitlab-ci.yml)"
# what the job actually sees
script:
- env | grep -E "^CI_(PIPELINE_SOURCE|COMMIT_REF_NAME|RUNNER)" | sortCommon causes
Variable not available at rule-evaluation time
Rules are evaluated when the pipeline is created. A job-level variables: value, or one set later in a script, is not yet defined when its own rules are checked.
Nested expansion not enabled or mis-keyed
Referencing one variable inside another ($$NAME or ${${NAME}}) only works with the supported nested-expansion syntax. A wrong form yields a literal string.
Over-escaping with $$
In .gitlab-ci.yml, $$ is a literal dollar sign. Using $$VAR when you meant $VAR makes GitLab emit a literal $VAR instead of expanding it.
How to fix it
Define rule variables at the right scope
Use predefined or top-level variables in rules, or rules:variables so the value exists when the rule runs.
deploy:
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
variables:
TARGET: production
script:
- echo "deploying to $TARGET"Escape literals deliberately
Use $$ only when you want a literal dollar sign reaching the shell; use a single $ to expand a CI variable.
script:
- echo "CI value: $CI_COMMIT_SHA" # expanded by GitLab
- echo "shell var: $$HOME" # literal $HOME, expanded by the shellHow to prevent it
- Test rule variables against predefined CI variables that exist at creation time.
- Use
rules:variablesto set values that conditionally apply. - Reserve
$$for intentional literal dollars passed to the shell.