@mayursingru
/^Error/ {print l?l RS RS $0: RS $0; l=x; next} --> If the line starts with the string "Error" then print l, but when it has a value, use l and two times the record separator (RS) which is a newline, and additionally the current line ($0). If l has no value at the moment, kust print one record separator and the current line. This will make sure, that you have an empty line as separator for the next block starting with "Error". Afterwards we set the value of l to nothing (l=x) and skip the rest of the awk script for this input line (next).
/^line/ {l=sprintf("%s%s", l, $0); next} --> If a line starts with "line", we store into the variable l the current line and if there have been former lines starting with "line", they will be already stored in l and just being appended. With this, you will not have every "line..." on its own row but concatenated them all into one, as your example output shows. We then skip the rest of the awk script for that line again (next).
/^[[:space:]]*$/ {next} --> If a line is "empty", but we recognized that in your example there are spaces in it which can be blanks or tabs or mixed at any number, we just skip them. [[:space:]] is a POSIX bracket expansions (see this
Regex Tutorial - POSIX Bracket Expressions). The rest of the awk script is skipped again.
{print} --> If none of the before 3 conditions matched, ie. a line does not start with Error, line or contains spaces (or none of them), we just print the rest of the lines, which in your example is the one starting with "The following ...".
END{ if(l){print l} }When the whole file has been worked through, we just check, if l still has a value in it (this should be a line starting with "line"), we have to print it now, since we formerly had a line starting with "Error" triggering this print which will not happen anymore.