Bash error: "parameter null or not set"

The `${VAR:?msg}` form aborts with "parameter null or not set" when VAR is unset or empty. Provide a value or use a different default form.

Error String

bash: VAR: parameter null or not set

Tldr

You explicitly guarded a variable with `${VAR:?message}`. The message fires because VAR is unset or empty. Either set the variable, or switch to `${VAR-default}` to allow empty values.

Cause

The `:?` operator is a deliberate assertion: "fail loudly if this is missing." It is the right choice for required configuration. If you only want a default, use `${VAR:-fallback}`.

Repro

: "${API_TOKEN:?API_TOKEN must be set}"
curl -H "Authorization: Bearer $API_TOKEN" https://api.example.com

Fix

# Set it before invoking:
export API_TOKEN=...
./script.sh

# Or use a fallback if the assertion isn't required:
: "${API_TOKEN:-anonymous}"

Explanation

Use `:?` for secrets and required IDs. Use `:-` (default) or `:=` (default + assign) for optional knobs.

Faq

Q

How do I supply a default instead of aborting?

A

Use `${VAR:-default}` to substitute a fallback, or `${VAR:=default}` to substitute and assign it back for later use.

Q

Why does it abort even though the variable exists?

A

The `:?` form treats an empty value the same as unset. Drop the colon (`${VAR?msg}`) to accept an empty string.

Deep Dive

Heading

This error is deliberate — something asked for it

Body

The message comes from the `${VAR:?message}` expansion, which is the one expansion form designed to abort. Bash prints the variable name, your message (or a default), and exits a non-interactive shell immediately. Unlike `set -u`, it fires on empty values too, because the colon in `:?` means "unset or empty". `${VAR?message}` without the colon aborts only when the variable is genuinely unset, which is the right choice when an empty string is a legitimate value.

Heading

Where the value should have come from

Body

In practice the variable is usually an environment variable that was never exported, a positional parameter used before checking `$#`, or a value assigned inside a pipeline subshell where the assignment cannot escape. Check the export with `env | grep NAME` in the same context the script runs in — cron and systemd units start with a much smaller environment than your login shell. For values produced by a command, prefer `NAME=$(cmd)` over piping into `read`, which runs in a subshell in most configurations.

Checklist

Locate the `${VAR:?...}` expansion the message names — it is intentional, not a bug in Bash.

Check whether the value should come from the environment, an argument, or a default.

Verify the variable is exported in the environment the script actually runs in.

Switch to `${VAR-...}` if an empty string is a valid value.