Jenkins Error Handling
Pipelines fail. Networks drop, servers restart, and tests flake. A well-built pipeline expects trouble and reacts with a plan: retry, wait, clean up, or alert. Error handling separates a fragile script from a dependable delivery system.
The Layman's View
Consider a delivery driver. If a road is closed, a good driver tries another route, waits a few minutes when traffic clears, and phones the customer when the package will arrive late. A poor driver turns around and goes home. Jenkins error handling gives your pipeline the habits of the good driver.
Build Results in Jenkins
| Result | Colour | Meaning |
|---|---|---|
| SUCCESS | Blue or green | Every step passed |
| UNSTABLE | Yellow | The build finished but tests failed or quality checks warned |
| FAILURE | Red | A step failed and stopped the run |
| ABORTED | Grey | A person or a timeout stopped the run |
The Error Handling Toolbox
step runs
|
+---------+----------+
| |
passes fails
| |
| +-----------+-----------+
| | | |
| retry catchError try/catch
| (try again) (mark & go) (custom reaction)
|
post block runs in every case: always, success, failure, unstable, cleanup
The retry Step
Retry repeats a block when it fails. Use it for flaky actions such as downloads and remote calls.
stage('Download') {
steps {
retry(3) {
sh 'curl -fO https://example.com/data.zip'
}
}
}Jenkins runs the command up to three times. The stage fails only when all three attempts fail.
The timeout Step
A hung process can hold an agent for hours. Timeout stops it after a set time.
timeout(time: 10, unit: 'MINUTES') {
sh './long-running-task.sh'
}Combine timeout with retry to attempt a slow action twice, each limited to five minutes.
retry(2) {
timeout(time: 5, unit: 'MINUTES') {
sh './sync.sh'
}
}try and catch
Scripted code and script blocks accept Groovy try, catch, and finally. This pattern lets you run custom actions when a step fails.
steps {
script {
try {
sh './risky-step.sh'
} catch (err) {
echo "Step failed: ${err.getMessage()}"
currentBuild.result = 'UNSTABLE'
} finally {
echo 'Cleaning temporary files'
}
}
}The catch block swallows the error, so the pipeline keeps running. Setting the result to UNSTABLE records that something went wrong.
The catchError Step
CatchError offers a simpler path in declarative pipelines. It lets you choose both the build result and the stage result.
stage('Optional Lint') {
steps {
catchError(buildResult: 'UNSTABLE', stageResult: 'FAILURE') {
sh './lint.sh'
}
}
}The stage shows red, the build shows yellow, and later stages still run. This fits checks that inform the team but should not block a release.
The post Section
The post block runs after stages finish. It handles cleanup and alerts in every outcome.
post {
always { junit 'target/surefire-reports/*.xml' }
success { echo 'Build passed' }
failure { mail to: 'team@example.com',
subject: "Failed: ${env.JOB_NAME}",
body: "See ${env.BUILD_URL}" }
unstable { echo 'Some tests failed' }
cleanup { deleteDir() }
}Post Conditions
| Condition | Runs When |
|---|---|
| always | Every time |
| success | The build succeeds |
| failure | The build fails |
| unstable | The build turns unstable |
| changed | The result differs from the last build |
| cleanup | After every other post condition |
Choosing the Right Tool
- Use retry for temporary problems that often fix themselves.
- Use timeout for steps that may hang.
- Use catchError for non-critical checks.
- Use try and catch when you need custom logic.
- Use post for cleanup and alerts.
Mistakes to Avoid
Retrying Everything
Retry hides real bugs. A test that fails randomly needs a fix, not endless repetition.
Swallowing Errors Silently
A catch block with no message and no result change leaves the team blind. Always log the error or set a result.
Skipping Cleanup
Leftover containers and temporary files pile up on agents. Place cleanup in post, which runs after failures too.
A Simple Policy for Your Team
Write down which failures stop a release and which failures only raise a warning. Unit test failures and build errors usually block a release. Style checks and optional reports usually warn. Encode that policy with catchError and the unstable result, so the pipeline enforces it without debate. Review the policy every quarter, since a warning that nobody reads turns into noise. Teams that agree on these rules early spend far less time arguing over red and yellow builds later.
