Jenkins – How to detect which parallel stage failed in a Jenkins declarative pipeline

Jenkinsjenkins-pipeline

My Jenkins pipeline runs several tasks in parallel. It appears that if one stage fails, all subsequent stages will run their failure post block (whether they actually failed or not). I don't know if this is by design or if I'm doing something wrong.

Note: This pipeline runs on a Windows node, hence the bat('exit /b 1')

pipeline {
    agent any

    stages {
        stage('Parallel steps') {
            parallel {
                stage('Successful stage') {
                    steps {
                        script { sleep 10 }
                    }
                    post {
                        failure {
                            echo('detected failure: Successful stage')
                        }
                    }
                }
                stage('Failure stage') {
                    steps {
                        script { bat('exit /b 1') }
                    }
                    post {
                        failure { 
                            echo('detected failure: Failure stage')
                        }
                    }
                }
            }
        }
    }
}

In the above pipeline, only 'Failure stage' fails, yet in the output I see this, indicating the failure conditional executed for both steps!

Started by user Doe, John
Running on WINDOWS_NODE in D:\workspace
[Successful stage] Sleeping for 10 sec
[Failure stage] [test] Running batch script
[Failure stage] D:\workspace>exit /b 1 
Post stage
[Pipeline] [Failure stage] echo
[Failure stage] detected failure: Failure stage
[Failure stage] Failed in branch Failure stage
Post stage
[Pipeline] [Successful stage] echo
[Successful stage] detected failure: Successful stage
ERROR: script returned exit code 1
Finished: FAILURE

What's the best way for me to detect which parallel stage failed and report it to the overall pipeline?

Best Answer

It looks like this is a known bug with Declarative Pipelines. I had to give up using the built-in post->failure block and use try/catch instead, which has its own problems:

  1. You have to catch and then re-throw the error in order to make the stage fail appropriately.
  2. The UI can get a little confusing, as the step that failed is no longer highlighted in red (but the error message is still in the log).
  3. The code is slightly less readable.

This code works correctly. Only the failing stage echoes "detected failure" instead of both.

pipeline {
    agent any

    stages {
        stage('Parallel steps') {
            parallel {
                stage('Successful stage') {
                    steps {
                        script {
                            try {
                                sleep 10 
                            } catch (e) {
                                echo('detected failure: Successful stage')
                                throw(e)
                            }
                        }
                    }
                }
                stage('Failure stage') {
                    steps {
                        script {
                            try {
                                bat('exit /b 1')
                            } catch (e) {
                                echo('detected failure: Failure stage')
                                throw(e)
                            }
                        }
                    }
                }
            }
        }
    }
}