Appending newlines to a variable to be "echo"ed out

To create a new line variable is a good start. But you should use it in a different way.
Percent expansion doesn't work quite well with newlines in variables, it can be done, but it's quite complex.

But delayed expansion flawlessly works with any characters

@echo off
setlocal EnableDelayedExpansion

(set \n=^
%=empty line=%
)

echo Line1!\n!Line2

set "multiline_var=First Line!\n!Second Line"

echo !multiline_var!

First of all, delayed expansion does not affect the immediately expanded new-lines (or correctly spoken, line-feed characters), because you are using immediate (%-)expansion for them.

The major issue is that %-expansion happens so early, which seems to interfere with recognition of line-breaks when a variable contains such.

Everything behind a line-break becomes dismissed every time the parser expands a %-variable containing line-breaks, so they must be escaped, leading to manifold escaping sequences. This can be demonstrated by the following script:

@echo off
setlocal EnableExtensions EnableDelayedExpansion

rem // Define a line-feed character:
set \n=^


rem // Define an escaped line-feed character:
set NL=^^^%\n%%\n%^%\n%%\n%
::rem // Redefine the escaped new-line character; it does not change, because the parser first expands `%`-variables before it recognises line-breaks in them:
::set NL=^^%NL%%NL%
rem // Define multi-escaped sequences needed when the same expanded value passes through the parser several times:
set NLNL=^^^^^^^^^^^^^^%NL%%NL%^^%NL%%NL%^^^^^^%NL%%NL%^^%NL%%NL%
set NLNLNL=^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^%NL%%NL%^^%NL%%NL%^^^^^^%NL%%NL%^^%NL%%NL%^^^^^^^^^^^^^^%NL%%NL%^^%NL%%NL%^^^^^^%NL%%NL%^^%NL%%NL%
::set NLNLNL=^^^^^^^^^^^^^^^^^^^^^^^^%NLNL%%NLNL%^^^^^^^^%NLNL%%NLNL%
echo  ---- DEFINITION ---- 
set NL
echo  -------------------- 

echo !\n! ***** IMMEDIATE EXPANSION ***** !\n!

rem // The more parser passes the more escaping is needed:
echo  -- VARIABLE CHECK -- 
set errMsg=This would be the first custom error message%NLNLNL%
set errMsg
set errMsg=%errMsg%This would be the second custom error message%NLNL%
set errMsg
set errMsg=%errMsg%This would be the third custom error message%NL%
set errMsg
echo  -- MESSAGE OUTPUT -- 
echo %errMsg%
echo  -------------------- 

echo !\n! ***** `CALL` EXPANSION ***** !\n!

echo  -- VARIABLE CHECK -- 
set errMsg=This would be the first custom error message%%NL%%
set errMsg
set errMsg=%errMsg%This would be the second custom error message%%NL%%
set errMsg
set errMsg=%errMsg%This would be the third custom error message%%NL%%
set errMsg
echo  -- MESSAGE OUTPUT -- 
call echo %errMsg%
echo  -------------------- 

echo !\n! ***** DELAYED EXPANSION ***** !\n!

echo  -- VARIABLE CHECK -- 
set errMsg=This would be the first custom error message!\n!
set errMsg
set errMsg=!errMsg!This would be the second custom error message!\n!
set errMsg
set errMsg=!errMsg!This would be the third custom error message!\n!
set errMsg
echo  -- MESSAGE OUTPUT -- 
echo !errMsg!
echo  -------------------- 

endlocal
exit /B

This is the related Command Prompt output:

 ---- DEFINITION ----
NL=^


NLNL=^^^^^^^

^

^^^

^


NLNLNL=^^^^^^^^^^^^^^^

^

^^^

^

^^^^^^^

^

^^^

^


 --------------------

 ***** IMMEDIATE EXPANSION *****

 -- VARIABLE CHECK --
errMsg=This would be the first custom error message^^^^^^^

^

^^^

^


errMsg=This would be the first custom error message^^^

^

This would be the second custom error message^^^

^


errMsg=This would be the first custom error message^

This would be the second custom error message^

This would be the third custom error message

 -- MESSAGE OUTPUT --
This would be the first custom error message
This would be the second custom error message
This would be the third custom error message
 --------------------

 ***** `CALL` EXPANSION *****

 -- VARIABLE CHECK --
errMsg=This would be the first custom error message%NL%
errMsg=This would be the first custom error message%NL%This would be the second custom error message%NL%
errMsg=This would be the first custom error message%NL%This would be the second custom error message%NL%This would be the third custom error message%NL%
 -- MESSAGE OUTPUT --
This would be the first custom error message
This would be the second custom error message
This would be the third custom error message

 --------------------

 ***** DELAYED EXPANSION *****

 -- VARIABLE CHECK --
errMsg=This would be the first custom error message

errMsg=This would be the first custom error message
This would be the second custom error message

errMsg=This would be the first custom error message
This would be the second custom error message
This would be the third custom error message

 -- MESSAGE OUTPUT --
This would be the first custom error message
This would be the second custom error message
This would be the third custom error message

 --------------------

In the IMMEDIATE EXPANSION section complicated escape sequences are required to maintain the line-breaks when command lines pass through the parsing process multiple times.

The CALL EXPANSION section illustrates a simpler way, using the call command, which introduces another parsing phase, which lets the literal string portion %NL% be expanded, which is actually contained in the errMsg variable rather than line-breaks. Note that call is slow and has got side-effects, like caret-(^-)doubling and loss of &, |, < and > under certain circumstances.

The best option is certainly to use delayed expansion as shown through the DELAYED EXPANSION section, because this allows the variable errMsg to actually contain the line-breaks and to safely expand them without being recognised by the parser too early.