How to handle errors in Bash

Handle errors in Bash with set -euo pipefail, trap ERR, exit codes, and explicit checks. A practical guide to making scripts fail loudly and safely.

Tldr

Start every script with `set -euo pipefail`. Add `trap 'echo "error on line $LINENO" >&2' ERR` to surface the failure point. For commands that may legitimately fail, handle the exit code explicitly with `if ! cmd; then ...; fi` instead of relying on set -e alone.

Intro

Bash defaults to "continue on error" — if a command fails, the next one runs anyway. That is rarely what you want for production scripts. Here is how to make Bash fail loudly, safely, and informatively.

Steps

Name

Enable strict mode

Text

The combination of -e (exit on error), -u (error on unset variable), and -o pipefail (pipeline fails if any stage fails) catches three of the biggest classes of silent failure.

Name

Add an ERR trap for context

Text

set -e tells you the script died but not where. A trap on ERR prints line number and the failing command before exiting.

Name

Handle expected failures explicitly

Text

Some commands may legitimately fail — a grep that finds nothing, a curl that may 404. Wrap them so they do not trigger set -e: use `if ! cmd; then ...; fi` or `cmd || true` if you genuinely do not care.

Name

Clean up on exit with trap EXIT

Text

Temp files, lock files, and other resources should be cleaned up whether the script succeeded or failed. Use a trap on EXIT so it runs no matter how the script ends.

Name

Return meaningful exit codes

Text

Convention: 0 = success, 1 = general error, 2 = misuse (bad arguments), 64-78 = sysexits.h codes (64 = usage, 65 = data, 77 = permission). Pick codes your callers can act on.

Name

Test your error paths

Text

set -e silently disables in some contexts (functions called from conditions, commands in pipelines on the left). Write a small test that triggers each failure mode and confirm the script aborts where you expect.

Faq

Q

Does set -e catch every error?

A

No. It is suspended inside if/while conditions, on the left of && and ||, inside functions called from those contexts, and in pipelines without pipefail. Always combine -e with -o pipefail and explicit checks for the trickier cases.

Q

Should I use set -e or check exit codes manually?

A

Use both. set -euo pipefail catches the cases you forgot to think about. Explicit if/then handles cases where failure is a valid outcome.

Q

How do I get a stack trace?

A

Bash 4+ provides BASH_SOURCE, BASH_LINENO, and FUNCNAME arrays. Loop over them in your ERR trap to print a call stack. There are several gist snippets — search "bash err trap stack trace".