Scripted Pipeline, Groovy Control Flow, node Blocks, Dynamic Logic, and When to Use Scripted Pipelines: Diagnostics, Failure Modes, Security, and Performance
Diagnose Scripted Pipeline failures by preserving source/build/queue/agent evidence, then separate CPS serialization, node/workspace, sandbox, controller-load, and external-state problems before applying the smallest repair.
Learning objectives
- Diagnose a Scripted Pipeline from first failure evidence rather than blind replay.
- Recognize non-serializable state retained across suspension points and repair the data model.
- Identify controller-heavy Groovy and move substantial work to agent-side processes.
- Treat sandbox rejection and unsandboxed approval as security evidence.
- Preserve dynamic stage identity and external side-effect context across retries/restarts.
1. Evidence-first diagnostic ladder
Scripted Pipeline is flexible enough that many failures look superficially similar. Preserve evidence before changing anything, then classify the causal layer.
- Record job full name, build number/URL, cause, exact Jenkinsfile/source SHA, and first failure timestamp.
- Record Jenkins core/Java and Pipeline: Groovy, Script Security, and Nodes/Processes versions.
-
Inspect queue item and node/label/executor eligibility if a
nodestep is waiting. - Record agent/Remoting/workspace/toolchain state if work reached an agent.
- Inspect the Pipeline stack trace and identify whether it is CPS/serialization, sandbox, step, shell/tool, credential, or external-system failure.
- Preserve artifacts/logs/external IDs before retry, replay, restart, or cleanup.
- Change only the smallest causal layer and rerun the smallest safe scope.
2. Failure: heavy computation on the controller
Pipeline: Groovy runs orchestration code in the controller process. A Jenkinsfile that parses a huge dataset, performs CPU-heavy sorting, or holds giant object graphs can compete with scheduling and other Pipeline work.
// Poor design: large project computation inside Pipeline Groovy.
def rows = readFile('huge.csv').readLines()
def ranked = rows.collect { parseComplexRecord(it) }.sort { a, b -> b.score <=> a.score }
echo "top=${ranked.take(100)}"
The repair is not “add more controller heap” first. Move the computation to an agent-side program:
node('lab-linux') {
checkout scm
sh './tools/rank-data huge.csv > ranked.json'
archiveArtifacts artifacts: 'ranked.json'
}
Measure controller CPU/heap/GC and Pipeline latency before and after if performance is the incident.
3. Intentionally broken example: non-serializable state across a suspension
A regex Matcher is a classic example of an object you
should not retain across Pipeline suspension.
def matcher = ('release-42' =~ /release-(\d+)/)
sleep 2
echo "release=${matcher[0][1]}"
At or around the suspension/persistence point, Jenkins may report a
serialization failure such as
java.io.NotSerializableException: java.util.regex.Matcher. Preserve the stack trace and exact Jenkinsfile SHA.
Repair the data model, not the symptom:
def releaseId = ('release-42' =~ /release-(\d+)/)[0][1].toString()
sleep 2
echo "release=${releaseId}"
Only the serializable string survives. Do not mark the entire
Pipeline @NonCPS; that would violate Pipeline step
semantics.
4. Failure: CPS method mismatch
A common mistake is calling Pipeline steps from
@NonCPS code:
@NonCPS
def wrongHelper() {
echo 'This is a Pipeline step and does not belong here.'
}
wrongHelper()
The fix is to return data from the pure helper and call the Pipeline step from CPS-transformed code:
@NonCPS
def messageText() {
return 'computed safely'
}
echo messageText()
5. Failure: dynamic logic hides stage identity
If every component is processed inside one generic stage, a failure log may say only “Build failed” with no component boundary. Improve evidence by using validated, bounded stage names and recording component identity in artifacts/logs.
components.each { component ->
stage("Test ${component}") {
node('lab-linux') {
writeFile file: "evidence/${component}.txt", text: "component=${component}\n"
sh "./test-component '${component}'"
}
}
}
6. Failure: sandbox rejection
Suppose a Jenkinsfile tries to call an internal Jenkins singleton or filesystem API and Script Security rejects it. The unsafe response is to approve whatever signature appears until the build turns green. The safe response is to ask why build code needs that authority.
7. Failure: node context mistaken for controller context
stage('Broken') {
sh 'pwd'
}
In Scripted Pipeline, sh normally requires a
node/workspace context. If no node is allocated, Jenkins reports a
missing context rather than secretly running the shell on the
controller. Repair by placing the external process inside the
correct node block.
8. Failure: external side-effect identity lost across restart/rerun
Imagine a Scripted Pipeline creates synthetic release ID
R-104, then waits or the controller restarts. A rerun
that creates R-105 without checking existing state
duplicates the side effect. Persist the release ID/digest as build
evidence and reconcile the external target before repeating.
Pipeline durability preserves Jenkins program state; it cannot make an external API idempotent for you.
9. Failure taxonomy
| Symptom | Likely layer | Evidence | Smallest repair |
|---|---|---|---|
Waiting at node |
Queue/label/capacity | Queue reason, node labels, executor state | Correct label/capacity; do not edit Groovy logic blindly |
NotSerializableException |
CPS state | Stack trace + live variable path | Reduce to serializable value before suspension |
| Rejected signature | Script Security | Sandbox rejection text | Use supported API/step or reviewed abstraction |
| Controller CPU high | Controller-side Groovy/plugin load | Metrics/thread dump/build timing | Move heavy computation to agents and remeasure |
sh missing context |
Node/workspace scope | Pipeline stack trace | Acquire correct node/workspace |
| Duplicate external resource | External side effect/idempotency | External ID + build/source identity | Reconcile/create-or-update; do not blind-retry |
10. Performance guardrails
- Keep Groovy “glue” small; run build logic in agent processes.
- Do not create unbounded loops, closures, dynamic stages, or parallel maps from untrusted input.
- Release node executors during long waits when workspace continuity is unnecessary.
- Archive only bounded evidence; do not keep massive object graphs in Pipeline variables.
- Measure queue wait, controller CPU/heap/GC, step duration, and artifact I/O before tuning.
11. Minimal incident packet
Capture: job/build/source identity; cause; queue/node/workspace evidence; first stack trace; controller and agent timestamps; installed Pipeline/Script Security versions; sandbox rejection if relevant; dynamic stage/component identity; archived artifacts; and any external side-effect ID. Preserve this before replay or repair.
Knowledge check
What is the first action after a Scripted Pipeline fails?
Preserve job/build/source identity and first-failure evidence before replay, restart, or editing.
Why can a regex Matcher cause failure across
sleep?
It is not serializable, and Pipeline may need to persist reachable program state at suspension points.
What is the right repair for controller-heavy Groovy?
Move substantial project computation into an external program on an agent, then measure the controller again.
Should a rejected sandbox signature be approved automatically?
No. Treat it as a security boundary and prefer a supported/narrower mechanism.
Does Pipeline restartability make external API calls idempotent?
No. External side effects need their own identifiers, reconciliation, and idempotency design.
Official references and version notes
- Jenkins LTS changelog — current LTS and tested Java configurations.
-
Jenkins Pipeline handbook
— Scripted Pipeline fundamentals,
node, stages, and Jenkinsfile concepts. - Pipeline Syntax — Scripted control-flow examples and Pipeline syntax reference.
- Pipeline Best Practices — controller-side Groovy, serialization, and Pipeline scalability guidance.
-
Pipeline CPS Method Mismatches
— CPS transformation,
@NonCPS, serialization, and mismatch failure modes. - In-process Script Approval — Groovy Sandbox and script/signature approval security model.
- Pipeline: Groovy — current CPS execution engine implementation and compatibility.
- Script Security — current sandbox/approval plugin baseline and security history.
-
Pipeline: Nodes and Processes
—
node, workspace, and durable external-process support.
Rechecked on 2026-09-16. Examples assume
Jenkins 2.568.3 LTS, tested with Java 21 and 25,
Pipeline: Groovy 4380.v6eb_8378b_9647, Script
Security 1415.v9a_f9b_3a_c253d, and Pipeline:
Nodes and Processes 1479.v56e587f413a_7.
Pipeline: Groovy 4380 requires Jenkins 2.528.3 or newer. The
mandatory lab uses a disposable lab-linux agent, a
synthetic repository, no production credential, and no unsandboxed
approval. Record your actually installed versions because plugin
releases change independently from Jenkins core.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.