Duplicate writes in Jenkins Pipeline retry step during transient failures
0 reputation · 08 Oct 2025, 21:32 UTC
0 reputation · 08 Oct 2025, 21:32 UTC
The goal is to use the Jenkins Pipeline retry step to handle transient failures without causing duplicate writes to external resources such as files or database records.
The retry step re‑executes the entire block on any exception up to the configured count, and it does not provide a built‑in option to check whether a side‑effect has already succeeded or to skip re‑execution after a partial success.
Because Jenkins offers no native idempotency guarantee, users must make the block idempotent themselves or rely on the RETRY_COUNT environment variable, yet it remains unclear whether any core mechanism exists to detect prior successful writes automatically.
Is there a built‑in way to configure the retry step to avoid re‑executing the block when a write has already succeeded?
Can the retry step be extended to skip re‑execution based on detection of a prior successful side‑effect without requiring external tracking?
31275 reputation · 09 Oct 2025, 01:50 UTC
No. The retry step has no built-in option to detect that a side-effect already succeeded, and it cannot be configured or extended to skip re-execution based on prior writes. It is purely a control-flow wrapper: when the block throws, the entire block runs again, up to the configured count. Jenkins has no visibility into what your block did to a file, database, or API before it failed, so idempotency is entirely your responsibility — either inside the block or in the external system you write to.
The classic failure mode is a write that succeeds but whose confirmation is lost: the database insert commits, then the network drops before the response arrives, the step throws, and retry runs the block again — inserting a second row. The same applies to appending to files, publishing messages, or calling non-idempotent HTTP endpoints. retry catches any exception and re-executes unconditionally; it keeps no record of partial progress.
Make the retried block idempotent using one of these patterns:
fileExists; for databases, query for the record first. This works when the check and write target the same agent and the state is observable.${env.JOB_NAME}-${env.BUILD_NUMBER} — and treat a constraint violation as success rather than failure.retry, and keep one-time writes outside it, so a retry never re-runs the write at all.def idemKey = "${env.JOB_NAME}-${env.BUILD_NUMBER}".replaceAll('[^A-Za-z0-9-]', '-')
retry(3) {
if (!fileExists("done-${idemKey}.marker")) {
sh "./upload.sh --idempotency-key ${idemKey}"
writeFile file: "done-${idemKey}.marker", text: 'ok'
}
}Note that RETRY_COUNT only tells you which attempt is running — it does not tell you whether a previous attempt partially succeeded, so it cannot replace a state check.
On a test controller, run a retry(3) block that appends a line to a file and then throws on the first two attempts (e.g. if (env.RETRY_COUNT.toInteger() < 3) { error 'boom' }). You will see the line appended three times. Add a fileExists guard and repeat: the line appears once. This confirms both the problem and the fix on your exact Jenkins version.
This reflects long-standing behavior of the Pipeline Basic Steps plugin; there is no announced core mechanism for automatic side-effect detection. If your writes go to a remote API, the strongest fix is an idempotency key accepted by that API — local checks cannot see state on the far side of a failed request.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.