How to fix duplicate builds

Hi all. We’ve been struggling with Jenkins creating duplicate builds on our Jenkins server. We initially had pollScm setup on our Jenkinsfile but recently switched to webhooks hoping that would fix the problem but it hasn’t. The problem “seems” to stem from a webhook that’s supposed to trigger one branch causing a build on another branch IF that other branch is currently in the middle of a build due to an SCM change.

ie. branchA is properly triggered by a webhook. 10 mins later branchB is triggered by a webhook but branchA is still building, the webhook triggers a build on both branchA and branchB. My guess is that the webhook trigger for branchB causes a poll which finds that branchA hasn’t successfully built the most recent git commit so it queues up another build.

Note we are using a Multibranch Pipleline for this repo and have it connected to github for webhook events.

Here’s a poll log from a branch that started building but wasn’t supposed to

Started on Aug 18, 2025, 8:45:38 AM
Started by event from 140.82.115.89 ⇒ https://hq.edge.app:8080/github-webhook/ on Mon Aug 18 08:45:34 UTC 2025
Using strategy: SpecificRevisionBuildChooser
[poll] Last Built Revision: Revision d263f13235ced80ba8910dde6df8b3b758aa89ff (staging/ios-mirror)
The recommended git tool is: NONE
using credential jenkins-github-app
��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Found 33 remote heads on https://github.com/EdgeApp/edge-tester.git
Ignoring refs/heads/master/ios as it doesn't match any of the configured refspecs
Ignoring refs/heads/testMaestro/ios-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/beta/ios-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/beta/ios as it doesn't match any of the configured refspecs
Ignoring refs/heads/master/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/master/android-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/test-gouda/android-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/beta/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/test-paneer/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/staging/ios as it doesn't match any of the configured refspecs
Ignoring refs/heads/test-cheddar/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/test-paneer/android-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/testMaestro/android-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/develop/ios-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/develop/android-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/master/ios-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/staging/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/staging/android-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/test-halloumi/android-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/develop/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/develop/ios as it doesn't match any of the configured refspecs
Ignoring refs/heads/coinhub/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/testMaestro/ios as it doesn't match any of the configured refspecs
Ignoring refs/heads/testMaestro/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/main as it doesn't match any of the configured refspecs
Ignoring refs/heads/beta/android-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/test-gouda/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/coinhub/android-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/test-feta/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/test-halloumi/android as it doesn't match any of the configured refspecs
Ignoring refs/heads/test-feta/android-mirror as it doesn't match any of the configured refspecs
Ignoring refs/heads/test-cheddar/android-mirror as it doesn't match any of the configured refspecs
[poll] Latest remote head revision on refs/heads/staging/ios-mirror is: 9136764ff397c6c3d73666ea29fc144c6c34c91d
Done. Took 3.1 sec
Changes found

Here’s our Jenkinsfile

def preTest() {
  deleteDir()
  checkout scm
  sh ‘adb version’
  sh ‘node -v’
  sh ‘which node’
  sh ‘yarn -v’
  sh ‘which yarn’
  sh ‘yarn’
  sh ‘printenv’
  withCredentials([
    file(credentialsId: ‘githubSshKey’, variable: ‘id_github’),
  ]) {
    sh “cp ${id_github} ./id_github”
  }
}

def postTest() {
  script {
    archiveArtifacts artifacts: ‘__debugOutput/**’, allowEmptyArchive: true
    def xmlFilter = “xmlreports/*.xml”
    echo ‘Junit tests’
    echo xmlFilter
    junit allowEmptyResults: true, testResults: xmlFilter
  }
}

pipeline {
  agent none
  options {
    timestamps()
    skipDefaultCheckout true
    overrideIndexTriggers true
    buildDiscarder logRotator(artifactDaysToKeepStr: ‘’, artifactNumToKeepStr: ‘’, daysToKeepStr: ‘7’, numToKeepStr: ‘10’)
  }
  triggers {
    githubPush()
    // pollSCM(‘H/5 * * * *’)
  }
  tools {
    nodejs ‘stable’
  }

  stages {
    stage(‘Debug Info’) {
      agent any
      steps {
        echo “Branch: ${env.BRANCH_NAME}”
        echo “Android check: ${env.BRANCH_NAME.endsWith(‘/android’)}”
        echo “iOS check: ${env.BRANCH_NAME.endsWith(‘/ios’)}”
        echo “Build cause: ${currentBuild.getBuildCauses()}”
        echo “Triggered by timer: ${currentBuild.getBuildCauses(‘hudson.triggers.TimerTrigger$TimerTriggerCause’).size() > 0}”
        echo “All build causes: ${currentBuild.getBuildCauses().collect { it?.class?.simpleName ?: ‘unknown’ }}”
        echo “SCM trigger detected: ${currentBuild.getBuildCauses(‘hudson.triggers.SCMTrigger$SCMTriggerCause’).size() > 0}”
        echo “Job Name: ${env.JOB_NAME}”
      }
    }
    stage(‘Parallel Stage’) {
      parallel {
        stage(‘Test Android Real Devices’) {
          when {
            beforeAgent true
            allOf {
              expression {
                env.BRANCH_NAME.endsWith(‘/android’)
              }
            }
          }
          agent { label ‘android-test-real’ }
          steps {
            script {
              retry(2) {
                echo “Running on ${env.NODE_NAME}”
                preTest()
                sh ‘yarn test android stripe’
              }
            }
          }
          post {
            always {
              postTest()
            }
          }
        }
        stage(‘Test iOS Simulator’) {
          when {
            beforeAgent true
            allOf {
              expression {
                env.BRANCH_NAME.endsWith(‘/ios’)
              }
            }
          }
          agent { label ‘ios-test-sim’ }
          steps {
            script {
              retry(2) {
                echo “Running on ${env.NODE_NAME}”
                preTest()
                sh ‘yarn test ios stripe’
              }
            }
          }
          post {
            always {
              postTest()
            }
          }
        }
        stage(‘Test Android Mirror - Daily’) {
          when {
            beforeAgent true
            allOf {
              expression {
                env.BRANCH_NAME.endsWith(‘/android-mirror’)
              }
            }
          }
          agent { label ‘android-test-real-mirror’ }
          steps {
            script {
              retry(2) {
                echo “Running daily Android mirror test on ${env.NODE_NAME}”
                preTest()
                sh ‘yarn test android mirror’
              }
            }
          }
          post {
            always {
              postTest()
            }
          }
        }
        stage(‘Test iOS Mirror - Daily’) {
          when {
            beforeAgent true
            allOf {
              expression {
                env.BRANCH_NAME.endsWith(‘/ios-mirror’)
              }
            }
          }
          agent { label ‘ios-test-sim’ }
          steps {
            script {
              retry(2) {
                echo “Running daily iOS mirror test on ${env.NODE_NAME}”
                preTest()
                sh ‘yarn test ios mirror’
              }
            }
          }
          post {
            always {
              postTest()
            }
          }
        }
      }
    }
  }

  post {
    success {
      echo ‘The force is strong with this one’
      script {
        slackSend(
          color: ‘#00FF00’,
          message: “TEST SUCCESS: Job ‘${env.JOB_NAME} [${env.BUILD_NUMBER}]’ (${env.BUILD_URL})“,
        )
      }
    }
    unstable {
      echo ‘Do or do not there is no try’
      script {
        slackSend(
          color: ‘#aacc00’,
          message: “TEST SUCCESS ON RETRY: Job ‘${env.JOB_NAME} [${env.BUILD_NUMBER}]’ (${env.BUILD_URL})“,
        )
      }
    }
    failure {
      echo ‘The dark side I sense in you.’
      script {
        // Assuming you’ve already configured Slack in Jenkins’ global configuration...
        slackSend(
          color: ‘danger’,
          message: “@channel TEST FAILED: Job ‘${env.JOB_NAME} [${env.BUILD_NUMBER}]’ (${env.BUILD_URL})“,
        )
      }
    }
  }
}

Jenkins setup:

Jenkins: 2.516.1
OS: Linux - 6.8.0-71-generic
Java: 21.0.8 - Ubuntu (OpenJDK 64-Bit Server VM)
---
analysis-model-api:13.8.0-902.v26f80296f743
antisamy-markup-formatter:173.v680e3a_b_69ff3
apache-httpcomponents-client-4-api:4.5.14-269.vfa_2321039a_83
apache-httpcomponents-client-5-api:5.5-157.vb_43ef2f6845e
asm-api:9.8-163.vb_2a_96d3f9c3c
bootstrap5-api:5.3.7-2
bouncycastle-api:2.30.1.81-264.v95c79c0e772c
branch-api:2.1244.vf95c81f1641c
build-name-setter:2.5.1
build-timeout:1.38
caffeine-api:3.2.2-178.v353b_8428ed56
checks-api:373.vfe7645102093
cloudbees-folder:6.1037.v4cb_8573b_72a_a_
commons-compress-api:1.28.0-1
commons-lang3-api:3.18.0-98.v3a_674c06072d
commons-text-api:1.14.0-194.v804a_dc3a_1b_d8
conditional-buildstep:1.5.0
config-file-provider:994.v3d4a_5fa_f353a_
configuration-as-code:1985.vdda_32d0c4ea_b_
copyartifact:770.va_6c69e063442
coverage:2.2879.v7434570a_8b_94
credentials:1419.v2337d1ceceef
credentials-binding:702.vfe613e537e88
dark-theme:574.va_19f05d54df5
dashboard-view:2.537.v5132851f6ca_f
data-tables-api:2.3.2-3
display-url-api:2.217.va_6b_de84cc74b_
dtkit-api:3.0.3
durable-task:595.ve87b_f1318d67
echarts-api:6.0.0-1
eddsa-api:0.3.0.1-19.vc432d923e5ee
email-ext:1925.v1598902b_58dd
font-awesome-api:7.0.0-1
forensics-api:3.1740.v1d0305a_2747b_
git:5.7.0
git-client:6.3.0
github:1.44.0
github-api:1.321-488.v9b_c0da_9533f8
github-branch-source:1834.v857721ea_74c6
gson-api:2.13.1-153.vb_3d0c48a_a_b_4a_
htmlpublisher:427
instance-identity:203.v15e81a_1b_7a_38
ionicons-api:94.vcc3065403257
jackson2-api:2.19.2-408.v18248a_324cfe
jakarta-activation-api:2.1.3-2
jakarta-mail-api:2.1.3-2
javax-activation-api:1.2.0-8
jaxb:2.3.9-133.vb_ec76a_73f706
jjwt-api:0.11.5-120.v0268cf544b_89
joda-time-api:2.14.0-149.v1c3ce991d1b_9
jquery3-api:3.7.1-583.vda_6ca_102270d
json-api:20250517-163.v1c5da_e99c775
json-path-api:2.9.0-178.vca_b_c71881321
jsoup:1.21.1-58.vfc578e6e2610
junit:1335.v6b_a_a_e18534e1
ldap:780.vcb_33c9a_e4332
mailer:515.vd788654779b_1
matrix-auth:3.2.7
matrix-project:856.v4c352b_3a_b_23e
metrics:4.2.32-481.v75f035fdc894
mina-sshd-api-common:2.15.0-161.vb_200831a_c15b_
mina-sshd-api-core:2.15.0-161.vb_200831a_c15b_
nodejs:1.6.5
okhttp-api:4.11.0-189.v976fa_d3379d6
parameterized-trigger:873.v8b_e37dd8418f
pipeline-build-step:571.v08a_fffd4b_0ce
pipeline-github-lib:65.v203688e7727e
pipeline-graph-view:619.v92f3fcdc39db_
pipeline-groovy-lib:752.vdddedf804e72
pipeline-input-step:534.v352f0a_e98918
pipeline-milestone-step:138.v78ca_76831a_43
pipeline-model-api:2.2265.v140e610fe9d5
pipeline-model-definition:2.2265.v140e610fe9d5
pipeline-model-extensions:2.2265.v140e610fe9d5
pipeline-stage-step:322.vecffa_99f371c
pipeline-stage-tags-metadata:2.2265.v140e610fe9d5
pipeline-utility-steps:2.19.0
plain-credentials:199.v9f8e1f741799
plugin-util-api:6.1154.ved7e7d3035a_5
prism-api:1.30.0-1
purge-build-queue-plugin:125.v09ee2147746f
resource-disposer:0.25
run-condition:243.v3c3f94e46a_8b_
scm-api:707.v749f968369d4
script-security:1378.vf25626395f49
slack:795.v4b_9705b_e6d47
snakeyaml-api:2.3-125.v4d77857a_b_402
ssh-credentials:361.vb_f6760818e8c
ssh-slaves:3.1071.v0d059c7b_c555
structs:353.v261ea_40a_80fb_
theme-manager:319.v9193461f9671
throttle-concurrents:2.18
timestamper:1.30
token-macro:477.vd4f0dc3cb_cf1
trilead-api:2.209.v0e69b_c43c245
variant:70.va_d9f17f859e0
warnings-ng:12.9749.va_2c1361722d3
workflow-aggregator:608.v67378e9d3db_1
workflow-api:1384.vdc05a_48f535f
workflow-basic-steps:1079.vce64b_a_929c5a_
workflow-cps:4183.v94b_6fd39da_c1
workflow-durable-task-step:1452.v0ee719c104a_7
workflow-job:1540.v295eccc9778f
workflow-multibranch:811.vcd33d074c2a_0
workflow-scm-step:437.v05a_f66b_e5ef8
workflow-step-api:706.v518c5dcb_24c0
workflow-support:976.vb_d9493c2eb_09
ws-cleanup:0.49
xunit:3.1.5