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.comFix
# 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.