Failing properly: --fail and exit codes

curl · Practical · 9 min

Scripts that treat an error page as success silently ship broken data; correct failure handling prevents that.

A surprise for many: curl considers an HTTP error response a successful transfer. curl https://api.example.com/missing returns exit code 0 while printing the 404 body.

To make HTTP errors fail:

  • -f / --fail makes curl exit with code 22 for status 400 and above and suppresses the body.
  • --fail-with-body (7.76+) also exits 22 but still outputs the body, which usually contains the useful error message from the API. Prefer it in scripts.

Other exit codes worth recognising:

  • 6 could not resolve host (DNS), 7 failed to connect (nothing listening or firewall),
  • 28 operation timed out, 35 TLS handshake problem, 60 certificate could not be verified,
  • 56 failure receiving data (connection reset).

man curl lists all of them under EXIT CODES, and with -sS curl prints a one-line explanation such as curl: (7) Failed to connect....

A robust script pattern is curl -sS --fail-with-body ... inside set -euo pipefail, so a failure stops the script with a readable error. When you need to branch on the status rather than fail, capture it with -w '%{http_code}', covered next. Without pipefail, curl ... | jq reports jq's exit status and hides curl's failure.

Example

curl -s https://api.example.com/missing; echo "exit $?"
# {"error":"not found"}  exit 0      <- looks like success!
curl -sf https://api.example.com/missing; echo "exit $?"
# exit 22                            (no body)
curl -sS --fail-with-body https://api.example.com/missing; echo "exit $?"
# {"error":"not found"}  exit 22
curl -sS http://127.0.0.1:1/; echo "exit $?"
# curl: (7) Failed to connect to 127.0.0.1 port 1 ... exit 7

set -euo pipefail
curl -sS --fail-with-body https://api.example.com/items | jq '.items | length'

Remember

  • HTTP 4xx/5xx is exit 0 unless you pass --fail or --fail-with-body
  • Exit 6 DNS, 7 connect, 22 HTTP error, 28 timeout, 60 certificate
  • Use set -o pipefail when piping curl into other tools

Watch out for

  • Using -f in scripts and then wondering why the API's error message is missing; use --fail-with-body.

Check yourself

  1. A script runs `curl -s URL > data.json` and the server answers 500. What is curl's exit code?
  2. Which exit code means curl could not resolve the host name?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: curl.se/docs/manpage.html#-f