Stop trap handlers from swallowing your exit codes
Technical Briefing | 10/16/2026
You probably use a trap exit script to clean up temporary files or notify your monitoring system when a script dies. It feels clean, it feels professional, but it is actually a trap. If you are not careful, that handler will overwrite the exit status of the command that actually failed, leaving you with a clean exit code 0 while the process is literally on fire.
The silent killer of debugging
When a shell script executes a trap, it runs in a subshell or a separate context where the exit code is whatever the last command inside the trap returned. If your cleanup logic does anything as simple as echo a status or remove a file, it resets the exit status to 0. This bit me hard back when I was relying on Jenkins to fail a build based on the return code. I spent four hours digging into why the build was marked as green despite the primary task crashing halfway through.
trap 'echo Cleaning up...; rm -f /tmp/data; exit $ret' EXIT
(do_something_important) || ret=$?
if [ -z "$ret" ]; then ret=0; fi
- Capture the status of the failing command immediately into a local variable
- Avoid using commands that might clobber the global exit status within the trap function
- Use an explicit exit with the captured variable to propagate the error code correctly
Most of the scripts I see on GitHub skip the variable assignment entirely. They just define the trap and pray the exit code passes through, which is pure luck of the draw. If you care about your pipeline actually detecting failures, you need to be explicit with that status variable. The next time you debug a script, check the trap handlers first; they are likely lying to your logs.
