It can be hard to tell if everything went okay inside a chain of pipes since $? will only report the return code from the final command in the chain.

Using a different variable ($PIPESTATUS) the return codes of all the commands chained together can be found.

#!/bin/bash

# as long as the last command in the pipe chain exits 0, RC will be set to 0
(exit 1) | (exit 2) | (exit 3) | true
echo "Pipe chain RC               : $?"

echo -e "\n# Try again, using PIPESTATUS afterwards to see all RCs"  
(exit 1) | (exit 2) | (exit 3) | true
# array of exit codes from the pipe chain
echo "Full PIPESTATUS array       : ${PIPESTATUS[@]}"

# uh oh, running a command has reset the array
echo "Post \"echo\" PIPESTATUS array: ${PIPESTATUS[@]}"

echo -e "\n# Try again, saving the PIPESTATUS"  
(exit 1) | (exit 2) | (exit 3) | true
SAVESTATUS=("${PIPESTATUS[@]}") # make a copy of the array

# uh oh, running a command has reset the array
echo "Post \"SAVE\" PIPESTATUS array: ${PIPESTATUS[@]}"

# but the copy is safe
echo "Full SAVESTATUS array       : ${SAVESTATUS[@]}"  
echo "Post \"echo\" SAVESTATUS array: ${SAVESTATUS[@]}"

set -o pipefail  
echo -e "\n# Try again, turn on the BASH option for pipefail"  
(exit 1) | (exit 2) | (exit 3) | true | echo 'Full pipeline still runs'
echo "Pipe chain RC               : $?"  

Produces the following output:

Pipe chain RC               : 0

# Try again, using PIPESTATUS afterwards to see all RCs
Full PIPESTATUS array       : 1 2 3 0  
Post "echo" PIPESTATUS array: 0

# Try again, saving the PIPESTATUS
Post "SAVE" PIPESTATUS array: 0  
Full SAVESTATUS array       : 1 2 3 0  
Post "echo" SAVESTATUS array: 1 2 3 0

# Try again, turn on the BASH option for pipefail
Full pipeline still runs  
Pipe chain RC               : 3