diff --git a/core/includes/errors.inc b/core/includes/errors.inc
index 1757acd..8945e1e 100644
--- a/core/includes/errors.inc
+++ b/core/includes/errors.inc
@@ -169,6 +169,9 @@ function _drupal_log_error($error, $fatal = FALSE) {
     }
   }
 
+  // Severity_level is not a valid replacement value for t().
+  unset($error['severity_level']);
+
   if (PHP_SAPI === 'cli') {
     if ($fatal) {
       // When called from CLI, simply output a plain text message.
diff --git a/core/lib/Drupal/Component/Utility/PlaceholderTrait.php b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
index e2db26b..fa92b27 100644
--- a/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
+++ b/core/lib/Drupal/Component/Utility/PlaceholderTrait.php
@@ -70,6 +70,17 @@ protected static function placeholderFormat($string, array $args) {
         case '@':
           // Escaped only.
           $args[$key] = static::placeholderEscape($value);
+          if (!SafeMarkup::isSafe($value)) {
+            $args[$key] = Html::escape($value);
+          }
+          break;
+
+        case '%':
+          // Escaped and placeholder.
+          if (!SafeMarkup::isSafe($value)) {
+            $value = Html::escape($value);
+          }
+          $args[$key] = '<em class="placeholder">' . $value . '</em>';
           break;
 
         case ':':
@@ -80,10 +91,8 @@ protected static function placeholderFormat($string, array $args) {
           $args[$key] = Html::escape(UrlHelper::stripDangerousProtocols($value));
           break;
 
-        case '%':
         default:
-          // Escaped and placeholder.
-          $args[$key] = '<em class="placeholder">' . static::placeholderEscape($value) . '</em>';
+          trigger_error('Invalid placeholder', E_USER_ERROR);
           break;
       }
     }
diff --git a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
index 279bdd4..b723bd3 100644
--- a/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
+++ b/core/tests/Drupal/Tests/Component/Utility/SafeMarkupTest.php
@@ -22,6 +22,20 @@
 class SafeMarkupTest extends UnitTestCase {
 
   /**
+   * The error message of the last error in the error handler.
+   *
+   * @var string
+   */
+  protected $lastErrorMessage;
+
+  /**
+   * The error number of the last error in the error handler.
+   *
+   * @var int
+   */
+  protected $lastErrorNumber;
+
+  /**
    * {@inheritdoc}
    */
   protected function tearDown() {
@@ -192,6 +206,41 @@ function providerCheckPlain() {
   }
 
   /**
+   * Custom error handler that saves the last error.
+   *
+   * We need this custom error handler because we cannot rely on the error to
+   * exception conversion as __toString is never allowed to leak any kind of
+   * exception.
+   *
+   * @param int $error_number
+   *   The error number.
+   * @param string $error_message
+   *   The error message.
+   */
+  public function errorHandler($error_number, $error_message) {
+    $this->lastErrorNumber = $error_number;
+    $this->lastErrorMessage = $error_message;
+  }
+
+  /**
+   * String formatting with SafeMarkup::format() and an unsupported placeholder.
+   *
+   * When you call SafeMarkup::format() with an unsupported placeholder, an
+   * InvalidArgumentException should be thrown.
+   */
+  public function testUnexpectedFormat() {
+
+    // We set a custom error handler because of https://github.com/sebastianbergmann/phpunit/issues/487
+    set_error_handler([$this, 'errorHandler']);
+    // We want this to trigger an error.
+    $error = SafeMarkup::format('Broken placeholder: ~placeholder', ['~placeholder' => 'broken'])->__toString();
+    restore_error_handler();
+
+    $this->assertEquals(E_USER_ERROR, $this->lastErrorNumber);
+    $this->assertEquals('Invalid placeholder', $this->lastErrorMessage);
+  }
+
+  /**
    * Tests string formatting with SafeMarkup::format().
    *
    * @dataProvider providerFormat
