Shell pipefail not set masks a pipeline failure in CI
By default a shell pipeline reports only the exit status of its last command. So failing-cmd | tee log returns tee's success and the step goes green. set -o pipefail makes the pipeline return the rightmost non-zero status instead.
What this error means
A build or test that clearly errored still passes CI. The command that failed was piped into tee, grep, head, or sort, whose success became the pipeline's exit code.
# without pipefail, this step exits 0 even though the build failed:
make build 2>&1 | tee build.log
# $? is tee's status, not make'sCommon causes
A failing command piped into a succeeding one
The default status is the last command's. Piping a build or test into tee/grep swallows the real failure because the trailing filter exits 0.
pipefail is not the default in most shells
Neither bash nor sh enables pipefail automatically, so CI steps that use pipes silently mask upstream failures unless it is set explicitly.
How to fix it
Enable pipefail
Set it at the top of the script (bash) so any failing stage fails the pipeline.
set -o pipefail
make build 2>&1 | tee build.logSet the CI shell to include pipefail
GitHub Actions bash steps run with pipefail by default, but a custom shell or explicit shell: sh may not. Set it explicitly to be safe.
defaults:
run:
shell: bash # runs with -eo pipefailHow to prevent it
- Add
set -o pipefailto every non-trivial script. - Keep the exit-critical command as the last stage where practical.
- Confirm your CI shell enables pipefail, especially with custom shells.