Best thing to do would be to catch the error in program 4 and handle it. If you want program 2 to handle it you can use QMHRSNEM (Resend escape message) to send the escape message up the call stack (either directly or ‘bubbling up’; see information about QMHRSNEM).
In the scenario you are describing, the exception is handled by the user through the inquiry message. When the user answers ‘C’ancel, program 4 is removed from the call stack and program 3 receives a new exception ‘Call to program 4 ended in error’. To find the original exception, you would have to examine the job log.