Exit Codes and Error Handling
Summary: Use exit codes to manage errors and success.
Robust error handling is crucial for building reliable software, whether you’re writing shell scripts, compiling code, or designing large-scale applications. Exit codes—a simple, standardized way for programs to communicate their status—are at the heart of effective error tracking and automation. In this post, we’ll explore what exit codes are, why they matter, and how to use them for precise error management.
What Are Exit Codes?
An exit code (also known as a return code or status code) is an integer value returned by a process to its parent when it terminates. This small piece of data is a powerful way for programs to signal whether they completed successfully or encountered an issue.
Typical Scenarios
- Shell Scripts: Check if a command executed correctly before proceeding.
- Automation: Orchestrate complex pipelines that react differently to success or failure.
- APIs and Programs: Signal specific errors to callers or wrappers for easier debugging.
Standard Exit Code Conventions
While you can technically use any number (usually 0-255 in Unix), there are conventions to make your code predictable and understandable:
Exit Code | Meaning |
---|---|
0 | Success (no error) |
1 | General error |
2 | Misuse of shell builtins |
126 | Command invoked cannot execute |
127 | Command not found |
128+n | Fatal error signal "n" |
130 | Script terminated by Ctrl+C |
... | As defined by your script |
Tip: Always return 0 for success. Non-zero codes indicate various failure modes.
Why Are Exit Codes Important?
- Automation: Scripts and orchestration tools rely on exit codes to make decisions. For example, in a CI/CD pipeline, a non-zero exit code can halt deployment.
- Debugging: Different exit codes help operators and developers understand what’s broken without sifting through logs.
- Scripting: Shell scripts and functions can use
if
statements to check for errors.
Checking and Using Exit Codes
In Bash Scripts
Every time a command runs, its exit code is stored in the $?
variable.
#!/bin/bash
cp file1.txt backup/
if [ $? -ne 0 ]; then
echo "File copy failed!"
exit 1
fi
echo "Backup succeeded."
exit 0
In Python
Python’s sys.exit()
lets you specify a custom exit code:
import sys
try:
result = 10 / 0
except ZeroDivisionError:
print("Math error!")
sys.exit(1)
print("Success!")
sys.exit(0)
In C
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *fp = fopen("file.txt", "r");
if (!fp) {
fprintf(stderr, "Could not open file\n");
return 1; // non-zero: error
}
// File operations...
fclose(fp);
return 0; // zero: success
}
How to Define and Document Your Exit Codes
For more complex applications, it’s good practice to define an exit code map in the documentation or a header file:
# exit_codes.sh
E_FILE_NOT_FOUND=10
E_CONFIG_ERROR=11
E_NETWORK_FAIL=12
Use these throughout your code for clarity and maintainability.
Best Practices for Exit Codes and Error Handling
- Be Consistent: Stick to standard codes where possible; use custom codes thoughtfully and document them.
- Document Clearly: Make sure your team knows what each exit code means.
- Test Error Paths: Don't just check success—simulate errors and ensure your exit codes are set properly.
- Avoid Collisions: If your script calls third-party tools, check what exit codes they return before defining your own custom codes.
Conclusion
Exit codes are more than just numbers—when used well, they provide a consistent, reliable communication channel between your processes, scripts, and automated systems. By implementing clear error handling with exit codes, you make your software predictable, debuggable, and easy to integrate into larger workflows. Start using exit codes as a fundamental part of your error handling strategy today!