CLOUD CONCEPT

Exit Codes

Exit codes are numeric status values that processes return when they terminate so calling systems can determine whether an operation succeeded or failed.

Cloud Concepts Hub →

What is Exit Codes

By convention, an exit code of zero indicates success, while a nonzero value indicates an error or another condition defined by the program. CI/CD pipelines use these values to evaluate automated tests, enforce deployment gates, and interpret health-check results, making correct propagation essential to reliable automation.

An exit code is a compact, machine-readable result produced when a process terminates. A shell, build runner, container runtime, or deployment agent can inspect that result and decide what to do next. Zero conventionally means successful completion; nonzero values have meanings defined by the application or execution environment. They are not universal error categories: the same number can mean different things for different tools, and some utilities use nonzero values for expected conditions rather than operational failures.

In CI/CD pipelines, exit codes connect automated testing to release decisions. A test runner should return zero when the required tests pass and nonzero when tests fail or cannot execute successfully. The build system then converts that command result into a job or action outcome. For example, an integration test can create a test record, read it through the application API, and verify the response. Returning nonzero when the response is incorrect allows the pipeline to stop before deploying an unsuitable artifact.

Measuring application health through exit codes requires a meaningful check, not merely a command that completes. A smoke-test script might verify that an endpoint responds within a deadline, returns the expected status, and contains an expected response field. Its exit code summarizes those assertions for the pipeline. An HTTP error response does not necessarily cause an HTTP client to exit nonzero: curl normally needs an option such as --fail to treat HTTP error responses as command failures. Application-level assertions remain necessary when an endpoint returns HTTP 200 with incorrect content.

Preserving failure status is a common implementation challenge. A wrapper script that runs tests and then successfully prints a message can accidentally return the printing command's status instead of the test result. Capture the test status immediately and explicitly return it after cleanup or report generation. Likewise, a shell pipeline that sends test output through tee may report only tee's success. In Bash, enabling pipefail makes a pipeline return a nonzero status when one of its commands fails. Automatic shell termination options have contextual exceptions, so critical validation paths should use deliberate status handling rather than assume every failure will stop execution.

Cloud build and deployment services apply these principles through their execution contracts. AWS CodeBuild normally treats an unhandled failing build command as a failure, while configured failure-handling behavior can change what happens next. AWS CodePipeline consumes the resulting action status rather than directly inspecting every test process. For EC2/on-premises deployments, an AWS CodeDeploy ValidateService hook can run a post-deployment smoke test and return nonzero when validation fails. Automatic rollback must be configured where required; a failed validation does not universally imply rollback.

For long-running applications, process termination and ongoing health are different signals. An application can remain running while serving errors, and a process can exit zero after an intentional shutdown without proving continued availability. Container health-check commands bridge this gap by periodically returning a status based on an active probe. In Amazon ECS, configured health-check retries, timeouts, and start periods influence how these results become container health status.

Exit codes are inexpensive and easy to automate, but they carry little diagnostic detail. Preserve logs, test reports, and relevant metrics alongside them. On Unix-like systems, shells commonly represent signal termination using 128 plus the signal number; a value such as 137 therefore suggests SIGKILL but does not alone prove an out-of-memory event. Define documented return-code contracts, distinguish retryable infrastructure problems from deterministic test failures, and keep retries bounded so persistent defects are not hidden.

Key aspects of Exit Codes

Reliable exit-code handling requires meaningful checks, preserved failure status, explicit pipeline policies, and supporting diagnostic evidence.

Explicit success and failure contracts

Use zero for successful completion and document the meaning of nonzero results for each tool or script. Do not assume a particular nonzero number has the same meaning across applications.

Reliable failure propagation

Ensure wrappers, cleanup steps, and output-processing commands preserve the original failure status. Use explicit status capture and appropriate shell pipeline handling so a successful logging command cannot hide a failed test.

Automated release gates

Translate required test and validation failures into nonzero process results that the CI/CD runner recognizes. Confirm the runner's failure-handling configuration actually blocks promotion rather than ignoring or tolerating those results.

Health checks based on assertions

Return status based on expected application behavior, including response content and latency where relevant. A running process or successfully executed HTTP request alone does not establish application health.

Context and diagnostic evidence

Pair exit codes with logs, test reports, and runtime events to identify the cause of failure. Distinguish application-defined errors from timeouts and signal termination before choosing retries or remediation.

Separate validation from recovery policy

Treat the exit code as an input to an orchestration decision, not as an automatic recovery mechanism. Configure retry limits, deployment rollback, and container replacement behavior independently.

Cloud services for Exit Codes

CodeBuild runs automated tests and exposes their outcome to CodePipeline, which controls whether a release proceeds. CodeDeploy can add deployment-time validation through lifecycle hooks on EC2/on-premises targets, with rollback configured separately. For container workloads, Amazon ECS uses health-check command results to assess ongoing health, while AWS Batch evaluates job completion and can apply exit-code-aware retry rules. Across these services, scripts must preserve failure status, checks must assert meaningful behavior, and logs or reports must explain the compact numeric result.

AWS CodeBuild image AWS CodeBuild

CodeBuild executes build and test commands and uses their outcomes when determining build status. Ensure test failures propagate through scripts and shell pipelines; publishing a test report is not a substitute for returning a failure status.

Open AWS CodeBuild →
AWS CodePipeline image AWS CodePipeline

CodePipeline uses action outcomes, such as a failed CodeBuild test action, to control progression through release stages. It does not directly interpret each application's exit code; the action provider translates execution results into action status.

Open AWS CodePipeline →
AWS CodeDeploy image AWS CodeDeploy

For EC2/on-premises deployments, CodeDeploy lifecycle hook scripts can use exit codes to report success or failure. A ValidateService hook can reject an unhealthy deployment through a failing smoke test, with automatic rollback governed by deployment configuration.

Open AWS CodeDeploy →
AWS Batch image AWS Batch

AWS Batch uses container execution outcomes to determine job success or failure. Retry strategies can match exit codes and other failure information to distinguish conditions worth retrying from failures that should terminate the job.

Open AWS Batch →