Skip to main content
DevOps Insightsguides & answers

Bash · troubleshooting

unbound variable

The script runs with set -u (often as set -euo pipefail) and read a variable or argument that was never set, so bash stopped. Set it, give it a default, or fail with a clear message.

Reproduced on bash 5.2.21, . Where the text says “reproduced”, the behaviour was run on that version, not taken from documentation.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 9 sections

Before you start

  • The script and the line number from the error, and where that variable is supposed to come from.

Run this first

step 1 of 1
bash -x script.sh

Look for The line just before the error

Prints each command before running it, so you see the last step before the unset variable.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

What does the error name?

Cause space

3 of 3 still possible

  • A variable the script expects was not providedCommon
  • The script was called without an argumentCommon
  • An empty array on an older bashOccasional

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    bash -x script.sh

    Prints each command before running it, so you see the last step before the unset variable.

    Look for The line just before the error

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

A variable the script expects was not provided

Common

An environment variable or secret not set in this CI job, step or environment, or a misspelled name.

Confirm

env | grep -c '^NAME=' 

0: the variable is not in the environment of this step

Fix

  • Provide it where the job is configured (the step's env, the environment's secrets), with the exact name.
  • If it is required, make the script say so: : "${NAME:?NAME must be set}" near the top.
  • If it is optional, read it as ${NAME:-} or give a real default: ${NAME:-staging}.
: "${ENVIRONMENT:?set ENVIRONMENT to staging or production}"

Reproduced on bash 5.2.21: stopped with this message when unset, and passed when set.

The script was called without an argument

Common

$1, $2 … are unset when fewer arguments are passed.

Confirm

grep -n '\$[0-9]' script.sh

$1 or similar used without a default

Fix

  • Check the count first: [ "$#" -ge 1 ] || { echo "usage: $0 ENV" >&2; exit 2; }.
  • Or use ${1:-default} where a default makes sense.

An empty array on an older bash

Occasional

macOS still ships bash 3.2, which treats "${ARR[@]}" on an empty array as unbound under set -u.

Confirm

bash --version | head -1

version 3.2

Fix

  • Use ${ARR[@]+"${ARR[@]}"}, which passed on both 3.2.57 and 5.2.21.
  • Or run the script with a current bash (Homebrew's on macOS) and point the shebang at it with #!/usr/bin/env bash.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

set -u tells bash to treat any unset variable as a mistake and stop, instead of quietly using an empty string. That is usually what you want in CI and deploy scripts. The error names the variable: either it really should have been set (a missing secret, a missing argument), or the script should say what to use when it is not.

Words used on this page

set -u (nounset)
Makes reading an unset variable an error that stops the script.
${VAR:-default}
The value of VAR, or default when VAR is unset or empty; allowed under set -u.
${VAR:?message}
Stops the script with message when VAR is unset or empty.
What is actually happening

Reproduced on 23 October 2026 with bash 5.2.21 on Ubuntu 24.04. With set -u, echo "deploying to $ENVIRONMENT" stopped the script with u.sh: line 2: ENVIRONMENT: unbound variable, exit 1, and nothing after that line ran. A script started without an argument stopped the same way on $1.

${ENVIRONMENT:-staging} and ${1:-none} ran normally, and : "${ENVIRONMENT:?set ENVIRONMENT to staging or production}" stopped with that message instead, which is the better choice for values that must be supplied.

Empty arrays depend on the bash version. "${ARR[@]}" on an empty array passed on bash 5.2.21 but failed with ARR[@]: unbound variable on macOS's bash 3.2.57, so a script can pass on a Linux runner and fail on a Mac.

How to tell this is your problem
WhereWhat you see
bash with set -u or set -euo pipefailscript.sh: line 12: DEPLOY_TOKEN: unbound variable
No argument passedscript.sh: line 3: $1: unbound variable
macOS bash 3.2, empty arrayscript.sh: line 5: ARGS[@]: unbound variable

How to know it is actually fixed

  • The script runs to the end with the variable set, and stops with your own message when it is not.

Stopping it happening again

  • Keep set -u: an empty variable passed to rm -rf "$DIR/" or a deploy target is worse than a stopped script.
  • Check required variables once, at the top, with ${VAR:?…}.
  • Run shellcheck in CI: SC2154 reports variables used but never assigned in the script (for uppercase names only with its optional check-unassigned-uppercase).

Security considerations

  • Do not fix a missing secret by giving it a default value in the script. Fail with ${VAR:?…} so a deploy never runs with a placeholder credential.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it