diff --git a/democlient/democlient.cpp b/democlient/democlient.cpp
index 0f0e7088cde..859882e0ce6 100644
--- a/democlient/democlient.cpp
+++ b/democlient/democlient.cpp
@@ -72,7 +72,9 @@ class CppcheckExecutor : public ErrorLogger {
 
     void reportOut(const std::string & /*outmsg*/, Color /*c*/) override {}
     void reportErr(const ErrorMessage &msg) override {
-        const std::string s = msg.toString(true);
+        static const std::string templateFormat = "{bold}{file}:{line}:{column}: {red}{inconclusive:{magenta}}{severity}:{inconclusive: inconclusive:}{default} {message} [{id}]{reset}\\n{code}";
+        static const std::string templateLocation = "{bold}{file}:{line}:{column}: {dim}note:{reset} {info}\\n{code}";
+        const std::string s = msg.toString(true, templateFormat, templateLocation);
 
         std::cout << s << std::endl;
 
diff --git a/lib/cppcheck.cpp b/lib/cppcheck.cpp
index 7024969117a..fbb6c4cce95 100644
--- a/lib/cppcheck.cpp
+++ b/lib/cppcheck.cpp
@@ -1867,6 +1867,7 @@ void CppCheck::purgedConfigurationMessage(const std::string &file, const std::st
 void CppCheck::getErrorMessages(ErrorLogger &errorlogger)
 {
     Settings settings;
+    settings.templateFormat = "{callstack}: ({severity}) {inconclusive:inconclusive: }{message}"; // TODO: get rid of this
     Suppressions supprs;
 
     CppCheck cppcheck(settings, supprs, errorlogger, true, nullptr);
diff --git a/lib/errorlogger.cpp b/lib/errorlogger.cpp
index 43f3369b5eb..b035856213f 100644
--- a/lib/errorlogger.cpp
+++ b/lib/errorlogger.cpp
@@ -610,30 +610,9 @@ static void replaceColors(std::string& source) {
     replace(source, substitutionMap);
 }
 
-// TODO: remove default parameters
 std::string ErrorMessage::toString(bool verbose, const std::string &templateFormat, const std::string &templateLocation) const
 {
-    // Save this ErrorMessage in plain text.
-
-    // TODO: should never happen - remove this
-    // No template is given
-    // (not 100%) equivalent templateFormat: {callstack} ({severity}{inconclusive:, inconclusive}) {message}
-    if (templateFormat.empty()) {
-        std::string text;
-        if (!callStack.empty()) {
-            text += ErrorLogger::callStackToString(callStack);
-            text += ": ";
-        }
-        if (severity != Severity::none) {
-            text += '(';
-            text += severityToString(severity);
-            if (certainty == Certainty::inconclusive)
-                text += ", inconclusive";
-            text += ") ";
-        }
-        text += (verbose ? mVerboseMessage : mShortMessage);
-        return text;
-    }
+    assert(!templateFormat.empty());
 
     // template is given. Reformat the output according to it
     std::string result = templateFormat;
diff --git a/lib/errorlogger.h b/lib/errorlogger.h
index 2d24e5f44fe..3f82986b01e 100644
--- a/lib/errorlogger.h
+++ b/lib/errorlogger.h
@@ -158,8 +158,8 @@ class CPPCHECKLIB ErrorMessage {
      * @return formatted string
      */
     std::string toString(bool verbose,
-                         const std::string &templateFormat = emptyString,
-                         const std::string &templateLocation = emptyString) const;
+                         const std::string &templateFormat,
+                         const std::string &templateLocation) const;
 
     std::string serialize() const;
     void deserialize(const std::string &data);
diff --git a/oss-fuzz/main.cpp b/oss-fuzz/main.cpp
index 358910595bc..dd8f77a80b1 100644
--- a/oss-fuzz/main.cpp
+++ b/oss-fuzz/main.cpp
@@ -49,6 +49,8 @@ static Settings create_settings()
 {
     // TODO: load std.cfg
     Settings s;
+    s.templateFormat = "{bold}{file}:{line}:{column}: {red}{inconclusive:{magenta}}{severity}:{inconclusive: inconclusive:}{default} {message} [{id}]{reset}\\n{code}";
+    s.templateLocation = "{bold}{file}:{line}:{column}: {dim}note:{reset} {info}\\n{code}";
     s.addEnabled("all");
     s.certainty.setEnabled(Certainty::inconclusive, true);
     return s;
diff --git a/test/fixture.cpp b/test/fixture.cpp
index 29a64e02647..3a4b685466a 100644
--- a/test/fixture.cpp
+++ b/test/fixture.cpp
@@ -429,7 +429,26 @@ void TestFixture::reportErr(const ErrorMessage &msg)
         return;
     if (msg.severity == Severity::information && msg.id == "normalCheckLevelMaxBranches")
         return;
-    const std::string errormessage(msg.toString(mVerbose, mTemplateFormat, mTemplateLocation));
+    std::string errormessage;
+    if (!mTemplateFormat.empty()) {
+        errormessage = msg.toString(mVerbose, mTemplateFormat, mTemplateLocation);
+    }
+    else {
+        if (!msg.callStack.empty()) {
+            // TODO: add column
+            errormessage += ErrorLogger::callStackToString(msg.callStack);
+            errormessage += ": ";
+        }
+        if (msg.severity != Severity::none) {
+            errormessage += '(';
+            errormessage += severityToString(msg.severity);
+            if (msg.certainty == Certainty::inconclusive)
+                errormessage += ", inconclusive";
+            errormessage += ") ";
+        }
+        errormessage += msg.shortMessage();
+        // TODO: add ID
+    }
     mErrout << errormessage << std::endl;
 }
 
diff --git a/test/testcppcheck.cpp b/test/testcppcheck.cpp
index aa9e90d15fa..78b343ffed0 100644
--- a/test/testcppcheck.cpp
+++ b/test/testcppcheck.cpp
@@ -38,6 +38,7 @@ class TestCppcheck : public TestFixture {
     TestCppcheck() : TestFixture("TestCppcheck") {}
 
 private:
+    const std::string templateFormat{"{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]"};
 
     class ErrorLogger2 : public ErrorLogger {
     public:
@@ -113,7 +114,8 @@ class TestCppcheck : public TestFixture {
                         "  return 0;\n"
                         "}");
 
-        const Settings s;
+        /*const*/ Settings s;
+        s.templateFormat = templateFormat;
         Suppressions supprs;
         ErrorLogger2 errorLogger;
         CppCheck cppcheck(s, supprs, errorLogger, false, {});
@@ -135,7 +137,8 @@ class TestCppcheck : public TestFixture {
                         "  return 0;\n"
                         "}");
 
-        const Settings s;
+        /*const*/ Settings s;
+        s.templateFormat = templateFormat;
         Suppressions supprs;
         ErrorLogger2 errorLogger;
         CppCheck cppcheck(s, supprs, errorLogger, false, {});
@@ -184,7 +187,9 @@ class TestCppcheck : public TestFixture {
         ScopedFile test_file_b("b.cpp",
                                "#include \"inc.h\"");
 
-        const Settings s;
+        /*const*/ Settings s;
+        // this is the "simple" format
+        s.templateFormat = templateFormat; // TODO: remove when we only longer rely on toString() in unique message handling
         Suppressions supprs;
         ErrorLogger2 errorLogger;
         CppCheck cppcheck(s, supprs, errorLogger, false, {});
@@ -215,9 +220,9 @@ class TestCppcheck : public TestFixture {
                              "(void)b;\n"
                              "}");
 
-        Settings s;
+        /*const*/ Settings s;
         // this is the "simple" format
-        s.templateFormat = "{file}:{line}:{column}: {severity}:{inconclusive:inconclusive:} {message} [{id}]";
+        s.templateFormat = templateFormat; // TODO: remove when we only longer rely on toString() in unique message handling?
         Suppressions supprs;
         ErrorLogger2 errorLogger;
         CppCheck cppcheck(s, supprs, errorLogger, false, {});
diff --git a/test/testerrorlogger.cpp b/test/testerrorlogger.cpp
index cdd74a95aa8..125e87aebd2 100644
--- a/test/testerrorlogger.cpp
+++ b/test/testerrorlogger.cpp
@@ -33,6 +33,8 @@ class TestErrorLogger : public TestFixture {
     TestErrorLogger() : TestFixture("TestErrorLogger") {}
 
 private:
+    const std::string templateFormat{"{callstack}: ({severity}) {inconclusive:inconclusive: }{message}"};
+
     const ErrorMessage::FileLocation fooCpp5{"foo.cpp", 5, 1};
     const ErrorMessage::FileLocation barCpp8{"bar.cpp", 8, 1};
     const ErrorMessage::FileLocation barCpp8_i{"bar.cpp", "รค", 8, 1};
@@ -81,13 +83,13 @@ class TestErrorLogger : public TestFixture {
         ErrorMessage message;
         message.id = id;
 
-        std::string serialized = message.toString(true, idPlaceholder + plainText + idPlaceholder);
+        std::string serialized = message.toString(true, idPlaceholder + plainText + idPlaceholder, "");
         ASSERT_EQUALS(id + plainText + id, serialized);
 
-        serialized = message.toString(true, idPlaceholder + idPlaceholder);
+        serialized = message.toString(true, idPlaceholder + idPlaceholder, "");
         ASSERT_EQUALS(id + id, serialized);
 
-        serialized = message.toString(true, plainText + idPlaceholder + plainText);
+        serialized = message.toString(true, plainText + idPlaceholder + plainText, "");
         ASSERT_EQUALS(plainText + id + plainText, serialized);
     }
 
@@ -133,8 +135,8 @@ class TestErrorLogger : public TestFixture {
         ASSERT_EQUALS(1, msg.callStack.size());
         ASSERT_EQUALS("Programming error.", msg.shortMessage());
         ASSERT_EQUALS("Programming error.", msg.verboseMessage());
-        ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(false));
-        ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(true));
+        ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(false, templateFormat, ""));
+        ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(true, templateFormat, ""));
     }
 
     void ErrorMessageConstructLocations() const {
@@ -143,8 +145,8 @@ class TestErrorLogger : public TestFixture {
         ASSERT_EQUALS(2, msg.callStack.size());
         ASSERT_EQUALS("Programming error.", msg.shortMessage());
         ASSERT_EQUALS("Programming error.", msg.verboseMessage());
-        ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(false));
-        ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(true));
+        ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(false, templateFormat, ""));
+        ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(true, templateFormat, ""));
     }
 
     void ErrorMessageVerbose() const {
@@ -153,8 +155,8 @@ class TestErrorLogger : public TestFixture {
         ASSERT_EQUALS(1, msg.callStack.size());
         ASSERT_EQUALS("Programming error.", msg.shortMessage());
         ASSERT_EQUALS("Verbose error", msg.verboseMessage());
-        ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(false));
-        ASSERT_EQUALS("[foo.cpp:5]: (error) Verbose error", msg.toString(true));
+        ASSERT_EQUALS("[foo.cpp:5]: (error) Programming error.", msg.toString(false, templateFormat, ""));
+        ASSERT_EQUALS("[foo.cpp:5]: (error) Verbose error", msg.toString(true, templateFormat, ""));
     }
 
     void ErrorMessageVerboseLocations() const {
@@ -163,8 +165,8 @@ class TestErrorLogger : public TestFixture {
         ASSERT_EQUALS(2, msg.callStack.size());
         ASSERT_EQUALS("Programming error.", msg.shortMessage());
         ASSERT_EQUALS("Verbose error", msg.verboseMessage());
-        ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(false));
-        ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Verbose error", msg.toString(true));
+        ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Programming error.", msg.toString(false, templateFormat, ""));
+        ASSERT_EQUALS("[foo.cpp:5] -> [bar.cpp:8]: (error) Verbose error", msg.toString(true, templateFormat, ""));
     }
 
     void ErrorMessageFromInternalError() const {
@@ -179,8 +181,8 @@ class TestErrorLogger : public TestFixture {
             ASSERT_EQUALS(0, loc.column);
             ASSERT_EQUALS("message", msg.shortMessage());
             ASSERT_EQUALS("message", msg.verboseMessage());
-            ASSERT_EQUALS("[file.c:0]: (error) message", msg.toString(false));
-            ASSERT_EQUALS("[file.c:0]: (error) message", msg.toString(true));
+            ASSERT_EQUALS("[file.c:0]: (error) message", msg.toString(false, templateFormat, ""));
+            ASSERT_EQUALS("[file.c:0]: (error) message", msg.toString(true, templateFormat, ""));
         }
         {
             InternalError internalError(nullptr, "message", "details", InternalError::INTERNAL);
@@ -192,8 +194,8 @@ class TestErrorLogger : public TestFixture {
             ASSERT_EQUALS(0, loc.column);
             ASSERT_EQUALS("msg: message", msg.shortMessage());
             ASSERT_EQUALS("msg: message: details", msg.verboseMessage());
-            ASSERT_EQUALS("[file.cpp:0]: (error) msg: message", msg.toString(false));
-            ASSERT_EQUALS("[file.cpp:0]: (error) msg: message: details", msg.toString(true));
+            ASSERT_EQUALS("[file.cpp:0]: (error) msg: message", msg.toString(false, templateFormat, ""));
+            ASSERT_EQUALS("[file.cpp:0]: (error) msg: message: details", msg.toString(true, templateFormat, ""));
         }
     }
 
@@ -207,7 +209,7 @@ class TestErrorLogger : public TestFixture {
         msg.classification = getClassification(msg.guideline, reportType);
         ASSERT_EQUALS("Advisory", msg.classification);
         ASSERT_EQUALS("2.8", msg.guideline);
-        ASSERT_EQUALS("Advisory 2.8", msg.toString(true, format));
+        ASSERT_EQUALS("Advisory 2.8", msg.toString(true, format, ""));
     }
 
     void ErrorMessageReportTypeCertC() const {
@@ -220,7 +222,7 @@ class TestErrorLogger : public TestFixture {
         msg.classification = getClassification(msg.guideline, reportType);
         ASSERT_EQUALS("L3", msg.classification);
         ASSERT_EQUALS("FIO42-C", msg.guideline);
-        ASSERT_EQUALS("L3 FIO42-C", msg.toString(true, format));
+        ASSERT_EQUALS("L3 FIO42-C", msg.toString(true, format, ""));
     }
 
     void CustomFormat() const {
@@ -229,8 +231,8 @@ class TestErrorLogger : public TestFixture {
         ASSERT_EQUALS(1, msg.callStack.size());
         ASSERT_EQUALS("Programming error.", msg.shortMessage());
         ASSERT_EQUALS("Verbose error", msg.verboseMessage());
-        ASSERT_EQUALS("foo.cpp:5,error,errorId,Programming error.", msg.toString(false, "{file}:{line},{severity},{id},{message}"));
-        ASSERT_EQUALS("foo.cpp:5,error,errorId,Verbose error", msg.toString(true, "{file}:{line},{severity},{id},{message}"));
+        ASSERT_EQUALS("foo.cpp:5,error,errorId,Programming error.", msg.toString(false, "{file}:{line},{severity},{id},{message}", ""));
+        ASSERT_EQUALS("foo.cpp:5,error,errorId,Verbose error", msg.toString(true, "{file}:{line},{severity},{id},{message}", ""));
     }
 
     void CustomFormat2() const {
@@ -239,8 +241,8 @@ class TestErrorLogger : public TestFixture {
         ASSERT_EQUALS(1, msg.callStack.size());
         ASSERT_EQUALS("Programming error.", msg.shortMessage());
         ASSERT_EQUALS("Verbose error", msg.verboseMessage());
-        ASSERT_EQUALS("Programming error. - foo.cpp(5):(error,errorId)", msg.toString(false, "{message} - {file}({line}):({severity},{id})"));
-        ASSERT_EQUALS("Verbose error - foo.cpp(5):(error,errorId)", msg.toString(true, "{message} - {file}({line}):({severity},{id})"));
+        ASSERT_EQUALS("Programming error. - foo.cpp(5):(error,errorId)", msg.toString(false, "{message} - {file}({line}):({severity},{id})", ""));
+        ASSERT_EQUALS("Verbose error - foo.cpp(5):(error,errorId)", msg.toString(true, "{message} - {file}({line}):({severity},{id})", ""));
     }
 
     void CustomFormatLocations() const {
@@ -250,8 +252,8 @@ class TestErrorLogger : public TestFixture {
         ASSERT_EQUALS(2, msg.callStack.size());
         ASSERT_EQUALS("Programming error.", msg.shortMessage());
         ASSERT_EQUALS("Verbose error", msg.verboseMessage());
-        ASSERT_EQUALS("Programming error. - bar.cpp(8):(error,errorId)", msg.toString(false, "{message} - {file}({line}):({severity},{id})"));
-        ASSERT_EQUALS("Verbose error - bar.cpp(8):(error,errorId)", msg.toString(true, "{message} - {file}({line}):({severity},{id})"));
+        ASSERT_EQUALS("Programming error. - bar.cpp(8):(error,errorId)", msg.toString(false, "{message} - {file}({line}):({severity},{id})", ""));
+        ASSERT_EQUALS("Verbose error - bar.cpp(8):(error,errorId)", msg.toString(true, "{message} - {file}({line}):({severity},{id})", ""));
     }
 
     void ToXmlV2() const {
diff --git a/test/testexecutor.cpp b/test/testexecutor.cpp
index b2d69d78b26..e11f563b553 100644
--- a/test/testexecutor.cpp
+++ b/test/testexecutor.cpp
@@ -47,34 +47,9 @@ class TestExecutor : public TestFixture {
 
 private:
     void run() override {
-        TEST_CASE(hasToLogDefault);
         TEST_CASE(hasToLogSimple);
     }
 
-    void hasToLogDefault() {
-        const std::list<FileWithDetails> files{FileWithDetails{"test.c"}};
-        const std::list<FileSettings> fileSettings;
-        Suppressions supprs;
-        DummyExecutor executor(files, fileSettings, settingsDefault, supprs, *this);
-
-        ErrorMessage::FileLocation loc1("test.c", 1, 2);
-        ErrorMessage msg({std::move(loc1)}, "test.c", Severity::error, "error", "id", Certainty::normal);
-
-        ASSERT(executor.hasToLog_(msg));
-        ASSERT(!executor.hasToLog_(msg));
-
-        ErrorMessage::FileLocation loc2("test.c", 1, 12);
-        msg.callStack = {std::move(loc2)};
-
-        // TODO: the default message does not include the column
-        TODO_ASSERT(executor.hasToLog_(msg));
-
-        msg.id = "id2";
-
-        // TODO: the default message does not include the id
-        TODO_ASSERT(executor.hasToLog_(msg));
-    }
-
     void hasToLogSimple() {
         const std::list<FileWithDetails> files{FileWithDetails{"test.c"}};
         const std::list<FileSettings> fileSettings;
diff --git a/test/testprocessexecutor.cpp b/test/testprocessexecutor.cpp
index 56cbb640cfc..aa350557d06 100644
--- a/test/testprocessexecutor.cpp
+++ b/test/testprocessexecutor.cpp
@@ -95,6 +95,7 @@ class TestProcessExecutorBase : public TestFixture {
         s.quiet = opt.quiet;
         if (opt.plistOutput)
             s.plistOutput = opt.plistOutput;
+        s.templateFormat = "{callstack}: ({severity}) {inconclusive:inconclusive: }{message}";
         Suppressions supprs;
 
         bool executeCommandCalled = false;
diff --git a/test/testsingleexecutor.cpp b/test/testsingleexecutor.cpp
index c2ad6d586b8..ac11ea1d64b 100644
--- a/test/testsingleexecutor.cpp
+++ b/test/testsingleexecutor.cpp
@@ -100,6 +100,7 @@ class TestSingleExecutorBase : public TestFixture {
         if (opt.plistOutput)
             s.plistOutput = opt.plistOutput;
         s.clangTidy = opt.clangTidy;
+        s.templateFormat = "{callstack}: ({severity}) {inconclusive:inconclusive: }{message}"; // TODO: remove when we only longer rely on toString() in unique message handling?
 
         Suppressions supprs;
 
diff --git a/test/testsuppressions.cpp b/test/testsuppressions.cpp
index b630658a6bc..ecab7fa3586 100644
--- a/test/testsuppressions.cpp
+++ b/test/testsuppressions.cpp
@@ -44,6 +44,8 @@ class TestSuppressions : public TestFixture {
 
 private:
 
+    const std::string templateFormat{"{callstack}: ({severity}) {inconclusive:inconclusive: }{message}"};
+
     void run() override {
         TEST_CASE(suppressionsBadId1);
         TEST_CASE(suppressionsDosFormat);     // Ticket #1836
@@ -253,6 +255,7 @@ class TestSuppressions : public TestFixture {
         settings.severity.enable(Severity::information);
         if (suppression == "unusedFunction")
             settings.checks.setEnabled(Checks::unusedFunction, true);
+        settings.templateFormat = templateFormat;
 
         std::vector<std::unique_ptr<ScopedFile>> scopedfiles;
         scopedfiles.reserve(filelist.size());
@@ -294,6 +297,7 @@ class TestSuppressions : public TestFixture {
                                             $.quiet = true,
                                             $.inlineSuppressions = true);
         settings.severity.enable(Severity::information);
+        settings.templateFormat = templateFormat;
 
         Suppressions supprs;
         if (!suppression.empty()) {
@@ -340,6 +344,7 @@ class TestSuppressions : public TestFixture {
                                             $.quiet = true,
                                             $.inlineSuppressions = true);
         settings.severity.enable(Severity::information);
+        settings.templateFormat = templateFormat;
 
         Suppressions supprs;
         if (!suppression.empty()) {
@@ -1198,6 +1203,7 @@ class TestSuppressions : public TestFixture {
         Settings settings;
         settings.quiet = true;
         settings.exitCode = 1;
+        settings.templateFormat = templateFormat;
 
         Suppressions supprs;
         ASSERT_EQUALS("", supprs.nomsg.addSuppressionLine("uninitvar"));
@@ -1238,6 +1244,7 @@ class TestSuppressions : public TestFixture {
         settings.inlineSuppressions = true;
         settings.relativePaths = true;
         settings.basePaths.emplace_back("/somewhere");
+        settings.templateFormat = templateFormat;
         const char code[] =
             "struct Point\n"
             "{\n"
diff --git a/test/testthreadexecutor.cpp b/test/testthreadexecutor.cpp
index 419ce19d384..e4ebdc3b930 100644
--- a/test/testthreadexecutor.cpp
+++ b/test/testthreadexecutor.cpp
@@ -96,6 +96,7 @@ class TestThreadExecutorBase : public TestFixture {
         if (opt.plistOutput)
             s.plistOutput = opt.plistOutput;
         s.clangTidy = opt.clangTidy;
+        s.templateFormat = "{callstack}: ({severity}) {inconclusive:inconclusive: }{message}"; // TODO: remove when we only longer rely on toString() in unique message handling?
 
         Suppressions supprs;