What is the purpose of the --fail flag in Curl?
The --fail
flag in Curl is a crucial option that changes how Curl handles HTTP error status codes (4xx and 5xx responses). By default, Curl considers any HTTP response a "success" from a network perspective, regardless of the status code. The --fail
flag modifies this behavior to treat HTTP error responses as actual failures, causing Curl to exit with a non-zero status code.
Default Curl Behavior vs --fail Flag
Without --fail (Default Behavior)
By default, Curl will successfully complete a request and return exit code 0 even when encountering HTTP error status codes:
# This command will exit with code 0 even if the page returns 404
curl https://httpbin.org/status/404
echo "Exit code: $?"
# Output: Exit code: 0
# Default behavior with 500 error
curl https://httpbin.org/status/500
echo "Exit code: $?"
# Output: Exit code: 0
With --fail Flag
When using the --fail
flag, Curl will exit with a non-zero status code for HTTP errors:
# This command will exit with code 22 for 404 error
curl --fail https://httpbin.org/status/404
echo "Exit code: $?"
# Output: Exit code: 22
# This will also exit with code 22 for 500 error
curl --fail https://httpbin.org/status/500
echo "Exit code: $?"
# Output: Exit code: 22
HTTP Status Codes Affected by --fail
The --fail
flag specifically targets these HTTP status code ranges:
- 4xx Client Errors: 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, etc.
- 5xx Server Errors: 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, etc.
Status codes 1xx, 2xx, and 3xx are still considered successful and won't trigger the fail behavior.
Practical Use Cases
1. Script Error Handling
The --fail
flag is essential for robust shell scripts and automation:
#!/bin/bash
# Download a file and handle errors properly
if curl --fail -o "data.json" "https://api.example.com/data"; then
echo "Download successful"
# Process the file
cat data.json | jq '.'
else
echo "Download failed with exit code: $?"
exit 1
fi
2. API Health Checks
Use --fail
for monitoring API endpoints:
# Simple health check script
curl --fail --silent --max-time 10 https://api.example.com/health
if [ $? -eq 0 ]; then
echo "API is healthy"
else
echo "API health check failed"
# Send alert or take corrective action
fi
3. CI/CD Pipeline Integration
In continuous integration environments, --fail
ensures build failures on HTTP errors:
# In a CI/CD pipeline
curl --fail --silent --show-error \
--header "Authorization: Bearer $API_TOKEN" \
--data '{"deployment": "production"}' \
https://api.example.com/deploy
# If the API returns an error, the pipeline will fail appropriately
Combining --fail with Other Curl Options
Silent Mode with Error Display
# Hide progress bar but show errors
curl --fail --silent --show-error https://api.example.com/endpoint
Retry Logic with --fail
# Retry failed requests up to 3 times
curl --fail --retry 3 --retry-delay 2 https://api.example.com/data
Custom Headers and Authentication
# API request with authentication and error handling
curl --fail \
--header "Content-Type: application/json" \
--header "Authorization: Bearer $TOKEN" \
--data '{"query": "SELECT * FROM users"}' \
https://api.example.com/query
Programming Language Integration
Python with subprocess
import subprocess
import sys
def make_curl_request(url):
try:
result = subprocess.run([
'curl', '--fail', '--silent', '--show-error', url
], capture_output=True, text=True, check=True)
return result.stdout
except subprocess.CalledProcessError as e:
print(f"Curl request failed with exit code {e.returncode}")
print(f"Error output: {e.stderr}")
return None
# Usage
data = make_curl_request("https://api.example.com/data")
if data:
print("Request successful:", data)
else:
print("Request failed")
Node.js with child_process
const { exec } = require('child_process');
function makeCurlRequest(url) {
return new Promise((resolve, reject) => {
const command = `curl --fail --silent --show-error "${url}"`;
exec(command, (error, stdout, stderr) => {
if (error) {
reject({
exitCode: error.code,
stderr: stderr,
message: `Curl failed with exit code ${error.code}`
});
} else {
resolve(stdout);
}
});
});
}
// Usage
makeCurlRequest('https://api.example.com/data')
.then(data => console.log('Success:', data))
.catch(error => console.error('Request failed:', error.message));
Advanced Error Handling Patterns
Custom Exit Code Mapping
#!/bin/bash
make_request() {
local url=$1
local output_file=$2
curl --fail --silent --show-error --output "$output_file" "$url"
local exit_code=$?
case $exit_code in
0)
echo "Success: Data saved to $output_file"
;;
22)
echo "HTTP error: Server returned an error status"
;;
6)
echo "Network error: Couldn't resolve host"
;;
7)
echo "Network error: Failed to connect to host"
;;
28)
echo "Timeout error: Operation timed out"
;;
*)
echo "Unknown error: Exit code $exit_code"
;;
esac
return $exit_code
}
# Usage
make_request "https://api.example.com/data" "response.json"
Logging and Monitoring
#!/bin/bash
LOG_FILE="/var/log/api_monitor.log"
monitor_endpoint() {
local endpoint=$1
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
if curl --fail --silent --max-time 30 "$endpoint" >/dev/null; then
echo "[$timestamp] SUCCESS: $endpoint" >> "$LOG_FILE"
return 0
else
local exit_code=$?
echo "[$timestamp] FAILURE: $endpoint (exit code: $exit_code)" >> "$LOG_FILE"
# Send alert for critical endpoints
if [[ "$endpoint" == *"critical"* ]]; then
echo "Critical endpoint failure detected" | mail -s "API Alert" admin@example.com
fi
return $exit_code
fi
}
# Monitor multiple endpoints
endpoints=(
"https://api.example.com/health"
"https://api.example.com/critical/service"
"https://api.example.com/user/status"
)
for endpoint in "${endpoints[@]}"; do
monitor_endpoint "$endpoint"
done
When Not to Use --fail
There are scenarios where you might want to handle HTTP errors manually rather than using --fail
:
Custom Error Processing
# Get the HTTP status code for custom handling
http_code=$(curl --silent --output response.txt --write-out "%{http_code}" https://api.example.com/data)
case $http_code in
200)
echo "Success: Processing data"
cat response.txt | jq '.'
;;
401)
echo "Authentication required: Refreshing token"
# Custom token refresh logic
;;
429)
echo "Rate limited: Waiting before retry"
sleep 60
# Retry logic
;;
*)
echo "Unexpected status code: $http_code"
cat response.txt
;;
esac
Error Recovery Strategies
Exponential Backoff with --fail
#!/bin/bash
exponential_backoff_request() {
local url=$1
local max_attempts=5
local delay=1
for ((attempt=1; attempt<=max_attempts; attempt++)); do
echo "Attempt $attempt of $max_attempts"
if curl --fail --silent --show-error --max-time 30 "$url"; then
echo "Request successful on attempt $attempt"
return 0
fi
if [ $attempt -lt $max_attempts ]; then
echo "Request failed, waiting ${delay}s before retry..."
sleep $delay
delay=$((delay * 2)) # Exponential backoff
fi
done
echo "All attempts failed"
return 1
}
# Usage
exponential_backoff_request "https://api.example.com/data"
Fallback Endpoints
#!/bin/bash
request_with_fallback() {
local primary_url=$1
local fallback_url=$2
local output_file=$3
echo "Trying primary endpoint: $primary_url"
if curl --fail --silent --show-error --output "$output_file" "$primary_url"; then
echo "Primary endpoint successful"
return 0
fi
echo "Primary failed, trying fallback: $fallback_url"
if curl --fail --silent --show-error --output "$output_file" "$fallback_url"; then
echo "Fallback endpoint successful"
return 0
fi
echo "Both endpoints failed"
return 1
}
# Usage
request_with_fallback \
"https://api.example.com/data" \
"https://backup-api.example.com/data" \
"response.json"
Integration with Modern Tools
Using --fail in Docker Health Checks
FROM alpine:latest
RUN apk add --no-cache curl
COPY check-health.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/check-health.sh
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD curl --fail --silent http://localhost:8080/health || exit 1
CMD ["your-app"]
Kubernetes Liveness Probes
apiVersion: v1
kind: Pod
spec:
containers:
- name: app
image: your-app:latest
livenessProbe:
exec:
command:
- /bin/sh
- -c
- curl --fail --silent --max-time 5 http://localhost:8080/health
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
Best Practices Summary
- Always use
--fail
in scripts where HTTP errors should cause script failure - Combine with
--silent --show-error
to suppress progress but show error messages - Use appropriate timeouts with
--max-time
to prevent hanging - Implement retry logic for transient failures
- Log both successes and failures for monitoring and debugging
- Consider fallback mechanisms for critical operations
- Use exponential backoff for retry strategies
- Monitor and alert on repeated failures
The --fail
flag is an essential tool for creating robust, production-ready scripts and automation workflows that properly handle HTTP error conditions. When building applications that need to monitor network requests in Puppeteer or handle errors in Puppeteer, understanding proper error handling patterns like those demonstrated with Curl's --fail
flag becomes invaluable for creating reliable web scraping and API interaction workflows.