Bash: Unbound Variable (tested)

This error only appears when a script has opted into strict mode with set -u (or set -o nounset). Without it, bash silently treats an undefined variable as an empty string; with it, referencing one is a hard error that stops the script — which is exactly the point of enabling it.

1. Reproduced: the error only fires under set -u

bash -c 'set -u; echo "$UNDEFINED_VAR"'
bash: line 1: UNDEFINED_VAR: unbound variable

The identical reference without set -u doesn't error at all — it just prints nothing and exits 0:

bash -c 'echo "$UNDEFINED_VAR2"; echo "exit: $?"'

exit: 0

This is the whole reason set -u exists: a typo'd variable name silently becoming an empty string is a common, hard-to-notice source of bugs (e.g. rm -rf "$TMPDIR/"* when TMPDIR is unset deletes from the current directory instead). set -u trades that silent failure for a loud one.

2. Reproduced: it also catches missing function arguments

This isn't limited to environment variables — a function called without its expected positional argument triggers the same error:

bash -c 'set -u; myfunc() { echo "$1"; }; myfunc'
environment: line 1: $1: unbound variable

This is a common real-world trigger: a function assumes a caller always passes an argument, and under set -u that assumption gets enforced automatically the first time it's violated.

3. Fix 1: provide a default with ${VAR:-default}

If the variable being unset sometimes is expected and fine, give it a fallback instead of removing set -u entirely:

bash -c 'set -u; echo "${UNDEFINED_VAR3:-fallback}"'
fallback

We confirmed this suppresses the error and substitutes the default, while set -u stays in effect for every other variable in the script — this is almost always the better fix than disabling strict mode outright, since it stays specific to the one variable you've reasoned about.

4. Fix 2: check before using, with an explicit guard

if [ -z "${MY_VAR:-}" ]; then
  echo "MY_VAR is required but not set" >&2
  exit 1
fi

The ${MY_VAR:-} form (empty default, not a real fallback value) lets you test for "is this set" without triggering the unbound-variable error during the check itself — useful when you want to fail with your own clear message rather than bash's generic one.

5. Fix 3 (last resort): turn off nounset for a specific section

If a specific block genuinely needs to tolerate unset variables (e.g. sourcing a third-party script you don't control), scope the exception narrowly instead of removing set -u from the whole script:

set +u
source ./third_party_script.sh
set -u

Turning it off for the whole script defeats the reason you added it in the first place; turning it off for one known block, then back on immediately after, keeps the protection everywhere else.

6. Why the function case matters more than it looks

The positional-argument case in section 2 is worth calling out separately from the plain-variable case: it means set -u doesn't just protect against typo'd global/env variable names, it also enforces that every function in your script is called with all the arguments it expects to reference. If you're migrating an existing script to add set -u, expect to find and fix a few of these in functions that were quietly relying on $2 or $3 being empty rather than absent.

Frequently asked questions

Why doesn't this error happen in my script even though I reference undefined variables?

Because set -u (or set -o nounset) isn't on by default in bash. We confirmed the identical reference to an undefined variable produces no error and exits 0 without it, versus a hard 'unbound variable' error with it enabled.

What's the safest fix when a variable is only sometimes set?

${VAR:-default} for a real fallback value, or ${VAR:-} (empty default) inside an explicit -z check if you want to detect and handle the unset case yourself with a clearer error message. We tested both and confirmed each suppresses the error for that one variable while leaving set -u active for everything else.

Does set -u only catch environment/global variables?

No — we reproduced it on a missing function argument too (myfunc() { echo "$1"; }; myfunc with no argument given). Any unset parameter reference triggers it, including $1/$2/etc. inside functions and scripts.

Is turning off set -u entirely a good fix?

Only as a last resort, and ideally scoped narrowly (set +u ... set -u around just the problem section) rather than removed from the whole script — removing it globally reintroduces the exact silent-failure risk (e.g. a typo'd variable in a destructive command like rm -rf) that set -u exists to catch.

Should every bash script use set -u?

It's widely recommended alongside set -e and set -o pipefail (often combined as set -euo pipefail) specifically because it turns typo'd/unset variable bugs into immediate, loud failures at the point of the mistake instead of silent wrong behavior later in the script.

Written with AI assistance. Measured on 2026-10-03 with GNU bash (Debian), running real scripts with set -u. Every error message below is copy-pasted output, not paraphrased. PlainHeaders is our extension; bash is free.