diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 743c3f535..5c582f0f6 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -748,14 +748,16 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessor::Result* re
 
     if (! m_placeholder_parser_failed_templates.empty()) {
         // G-code export proceeded, but some of the PlaceholderParser substitutions failed.
+        //FIXME localize!
         std::string msg = std::string("G-code export to ") + path + " failed due to invalid custom G-code sections:\n\n";
-        for (const std::string &name : m_placeholder_parser_failed_templates)
-            msg += std::string("\t") + name + "\n";
+        for (const auto &name_and_error : m_placeholder_parser_failed_templates)
+            msg += name_and_error.first + "\n" + name_and_error.second + "\n";
         msg += "\nPlease inspect the file ";
         msg += path_tmp + " for error messages enclosed between\n";
         msg += "        !!!!! Failed to process the custom G-code template ...\n";
         msg += "and\n";
         msg += "        !!!!! End of an error report for the custom G-code template ...\n";
+        msg += "for all macro processing errors.";
         throw Slic3r::PlaceholderParserError(msg);
     }
 
@@ -1434,7 +1436,11 @@ std::string GCode::placeholder_parser_process(const std::string &name, const std
         return m_placeholder_parser.process(templ, current_extruder_id, config_override);
     } catch (std::runtime_error &err) {
         // Collect the names of failed template substitutions for error reporting.
-        m_placeholder_parser_failed_templates.insert(name);
+        auto it = m_placeholder_parser_failed_templates.find(name);
+        if (it == m_placeholder_parser_failed_templates.end())
+            // Only if there was no error reported for this template, store the first error message into the map to be reported.
+            // We don't want to collect error message for each and every occurence of a single custom G-code section.
+            m_placeholder_parser_failed_templates.insert(it, std::make_pair(name, std::string(err.what())));
         // Insert the macro error message into the G-code.
         return
             std::string("\n!!!!! Failed to process the custom G-code template ") + name + "\n" +
diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp
index 377b27381..8a8154976 100644
--- a/src/libslic3r/GCode.hpp
+++ b/src/libslic3r/GCode.hpp
@@ -19,6 +19,7 @@
 #include "GCode/ThumbnailData.hpp"
 
 #include <memory>
+#include <map>
 #include <string>
 
 #ifdef HAS_PRESSURE_EQUALIZER
@@ -323,7 +324,7 @@ private:
     GCodeWriter                         m_writer;
     PlaceholderParser                   m_placeholder_parser;
     // Collection of templates, on which the placeholder substitution failed.
-    std::set<std::string>               m_placeholder_parser_failed_templates;
+    std::map<std::string, std::string>  m_placeholder_parser_failed_templates;
     OozePrevention                      m_ooze_prevention;
     Wipe                                m_wipe;
     AvoidCrossingPerimeters             m_avoid_crossing_perimeters;
diff --git a/src/libslic3r/PlaceholderParser.cpp b/src/libslic3r/PlaceholderParser.cpp
index 9bbc2cd8d..8d4a88b14 100644
--- a/src/libslic3r/PlaceholderParser.cpp
+++ b/src/libslic3r/PlaceholderParser.cpp
@@ -6,6 +6,7 @@
 #include <iomanip>
 #include <sstream>
 #include <map>
+#include <boost/nowide/convert.hpp>
 #ifdef _MSC_VER
     #include <stdlib.h>  // provides **_environ
 #else
@@ -870,7 +871,9 @@ namespace client
                 }
             }
             msg += '\n';
-            msg += error_line;
+            // This hack removes all non-UTF8 characters from the source line, so that the upstream wxWidgets conversions
+            // from UTF8 to UTF16 don't bail out.
+            msg += boost::nowide::narrow(boost::nowide::widen(error_line));
             msg += '\n';
             for (size_t i = 0; i < error_pos; ++ i)
                 msg += ' ';
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.cpp b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
index 1d309299a..0e51ca77c 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.cpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.cpp
@@ -68,9 +68,10 @@ bool SlicingProcessCompletedEvent::invalidate_plater() const
 	return false;
 }
 
-std::string SlicingProcessCompletedEvent::format_error_message() const
+std::pair<std::string, bool> SlicingProcessCompletedEvent::format_error_message() const
 {
 	std::string error;
+	bool        monospace = false;
 	try {
 		this->rethrow_exception();
     } catch (const std::bad_alloc& ex) {
@@ -78,12 +79,15 @@ std::string SlicingProcessCompletedEvent::format_error_message() const
                               "If you are sure you have enough RAM on your system, this may also be a bug and we would "
                               "be glad if you reported it."))) % SLIC3R_APP_NAME).str());
         error = std::string(errmsg.ToUTF8()) + "\n\n" + std::string(ex.what());
+    } catch (PlaceholderParserError &ex) {
+		error = ex.what();
+		monospace = true;
     } catch (std::exception &ex) {
 		error = ex.what();
 	} catch (...) {
 		error = "Unknown C++ exception.";
 	}
-	return error;
+	return std::make_pair(std::move(error), monospace);
 }
 
 BackgroundSlicingProcess::BackgroundSlicingProcess()
diff --git a/src/slic3r/GUI/BackgroundSlicingProcess.hpp b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
index d4fc3ddcc..b3f8a0a6b 100644
--- a/src/slic3r/GUI/BackgroundSlicingProcess.hpp
+++ b/src/slic3r/GUI/BackgroundSlicingProcess.hpp
@@ -57,7 +57,8 @@ public:
 	// Only valid if error()
 	void 		rethrow_exception() const { assert(this->error()); assert(m_exception); std::rethrow_exception(m_exception); }
 	// Produce a human readable message to be displayed by a notification or a message box.
-	std::string format_error_message() const;
+	// 2nd parameter defines whether the output should be displayed with a monospace font.
+	std::pair<std::string, bool> format_error_message() const;
 
 private:
 	StatusType 			m_status;
diff --git a/src/slic3r/GUI/MsgDialog.cpp b/src/slic3r/GUI/MsgDialog.cpp
index 5ca8aa49d..043224375 100644
--- a/src/slic3r/GUI/MsgDialog.cpp
+++ b/src/slic3r/GUI/MsgDialog.cpp
@@ -75,7 +75,7 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_
     // Text shown as HTML, so that mouse selection and Ctrl-V to copy will work.
     wxHtmlWindow* html = new wxHtmlWindow(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxHW_SCROLLBAR_AUTO);
     {
-        html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), -1));
+        html->SetMinSize(wxSize(40 * wxGetApp().em_unit(), monospaced_font ? 30 * wxGetApp().em_unit() : -1));
         wxFont 	  	font 			= wxSystemSettings::GetFont(wxSYS_DEFAULT_GUI_FONT);
 		wxFont      monospace       = wxSystemSettings::GetFont(wxSYS_ANSI_FIXED_FONT);
 		wxColour  	text_clr  		= wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT);
@@ -100,7 +100,8 @@ ErrorDialog::ErrorDialog(wxWindow *parent, const wxString &msg, bool monospaced_
 	btn_ok->SetFocus();
 	btn_sizer->Add(btn_ok, 0, wxRIGHT, HORIZ_SPACING);
 
-	logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, 192));
+	// Use a small bitmap with monospaced font, as the error text will not be wrapped.
+	logo->SetBitmap(create_scaled_bitmap("PrusaSlicer_192px_grayscale.png", this, monospaced_font ? 48 : 192));
 
     SetMaxSize(wxSize(-1, CONTENT_MAX_HEIGHT*wxGetApp().em_unit()));
 	Fit();
diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp
index 178644af4..ba54702e2 100644
--- a/src/slic3r/GUI/Plater.cpp
+++ b/src/slic3r/GUI/Plater.cpp
@@ -3643,17 +3643,17 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
     // This bool stops showing export finished notification even when process_completed_with_error is false
     bool has_error = false;
     if (evt.error()) {
-        std::string message = evt.format_error_message();
+        std::pair<std::string, bool> message = evt.format_error_message();
         if (evt.critical_error()) {
             if (q->m_tracking_popup_menu)
                 // We don't want to pop-up a message box when tracking a pop-up menu.
                 // We postpone the error message instead.
-                q->m_tracking_popup_menu_error_message = message;
+                q->m_tracking_popup_menu_error_message = message.first;
             else
-                show_error(q, message);
+                show_error(q, message.first, message.second);
         } else
-		    notification_manager->push_slicing_error_notification(message, *q->get_current_canvas3D());
-        this->statusbar()->set_status_text(from_u8(message));
+		    notification_manager->push_slicing_error_notification(message.first, *q->get_current_canvas3D());
+        this->statusbar()->set_status_text(from_u8(message.first));
         if (evt.invalidate_plater())
         {
             const wxString invalid_str = _L("Invalid data");