GitHub Actions ${{ }} Eaten by the Shell in a run: Step
A run: script that needs a literal ${{ ... }} sequence, or a value containing characters the shell treats specially, gets rewritten by GitHub expression interpolation or by the shell before your command sees it.
What this error means
A run step prints the wrong text, or fails with a shell parse error, because GitHub substituted an expression you meant to keep literal, or the shell expanded a value that contained quotes, backticks, or a dollar sign.
- run: echo "template uses ${{ '${{' }} version ${{ '}}' }}"
# intended to print the literal "${{ version }}", but interpolation rewrote itCommon causes
GitHub interpolates ${{ }} before the shell runs
Every ${{ ... }} in a run: block is evaluated by GitHub first, then the result is handed to the shell. A literal ${{ you wanted to keep is consumed unless you escape it.
Interpolated values are pasted unquoted
An expression result containing spaces, quotes, backticks, or $ is spliced raw into the script. The shell then re-parses it, which can break the command or run unexpected substitutions.
How to fix it
Pass values through env, not inline
Bind the expression to an env var and reference $VAR in the script, so the shell receives a single safe value instead of spliced text.
- run: echo "Title is: $TITLE"
env:
TITLE: ${{ github.event.issue.title }}Escape a literal ${{ sequence
- To emit a literal ${{ , write ${{ '${{' }} so GitHub outputs the braces as a string.
- Quote interpolated values inside the script ("$VAR") so the shell does not re-split them.
- Prefer the env approach for any untrusted input to avoid script injection.
How to prevent it
- Bind context and event values to env vars, then use $VAR in run scripts.
- Never paste untrusted ${{ github.event.* }} directly into a shell command.
- Quote shell variables to stop word-splitting and globbing.