diff --git a/mysql-test/main/mysqlbinlog_row_minimal.result b/mysql-test/main/mysqlbinlog_row_minimal.result
index b3cee345428..c5b151bf818 100644
--- a/mysql-test/main/mysqlbinlog_row_minimal.result
+++ b/mysql-test/main/mysqlbinlog_row_minimal.result
@@ -396,3 +396,17 @@ ROLLBACK /* added by mysqlbinlog */;
 /*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
 /*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
 DROP TABLE t1,t2;
+#
+# MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log with system table
+#
+FLUSH BINARY LOGS;
+CREATE TABLE t1 (pk INT PRIMARY KEY);
+INSERT INTO t1 VALUES (1);
+INSERT INTO t1 VALUES (1), (2) ON DUPLICATE KEY UPDATE pk= pk + 10;
+FLUSH BINARY LOGS;
+DROP TABLE t1;
+SELECT * FROM t1;
+pk
+2
+11
+DROP TABLE t1;
diff --git a/mysql-test/main/mysqlbinlog_row_minimal.test b/mysql-test/main/mysqlbinlog_row_minimal.test
index 85b816e1be5..88f86cece15 100644
--- a/mysql-test/main/mysqlbinlog_row_minimal.test
+++ b/mysql-test/main/mysqlbinlog_row_minimal.test
@@ -72,3 +72,18 @@ FLUSH BINARY LOGS;
 
 DROP TABLE t1,t2;
 
+--echo #
+--echo # MDEV-16372 ER_BASE64_DECODE_ERROR upon replaying binary log with system table
+--echo #
+FLUSH BINARY LOGS;
+CREATE TABLE t1 (pk INT PRIMARY KEY);
+INSERT INTO t1 VALUES (1);
+INSERT INTO t1 VALUES (1), (2) ON DUPLICATE KEY UPDATE pk= pk + 10;
+
+--let $binlog = query_get_value(SHOW MASTER STATUS, File, 1)
+
+FLUSH BINARY LOGS;
+DROP TABLE t1;
+--exec $MYSQL_BINLOG --verbose $datadir/$binlog | $MYSQL test
+SELECT * FROM t1;
+DROP TABLE t1;
diff --git a/sql/log_event.cc b/sql/log_event.cc
index 28ac34c8a0f..be77fbc3091 100644
--- a/sql/log_event.cc
+++ b/sql/log_event.cc
@@ -3693,6 +3693,10 @@ void free_table_map_log_event(Table_map_log_event *event)
   delete event;
 }
 
+static
+bool describe_event(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
+                    Rows_log_event *ev);
+
 /**
   Encode the event, optionally per 'do_print_encoded' arg store the
   result into the argument cache; optionally per event_info's
@@ -3855,57 +3859,27 @@ bool Log_event::print_base64(IO_CACHE* file,
       break;
     }
 
-    if (ev)
+    if (print_event_info->inside_binlog && print_event_info->verbose)
+    {
+      if (ev)
+        print_event_info->verbose_events.append(ev);
+    }
+    else
     {
-      bool error= 0;
-
-#ifdef WHEN_FLASHBACK_REVIEW_READY
-      ev->need_flashback_review= need_flashback_review;
       if (print_event_info->verbose)
       {
-        if (ev->print_verbose(file, print_event_info))
-          goto err;
-      }
-      else
-      {
-        IO_CACHE tmp_cache;
-
-        if (open_cached_file(&tmp_cache, NULL, NULL, 0,
-                              MYF(MY_WME | MY_NABP)))
-        {
-          delete ev;
-          goto err;
-        }
-
-        error= ev->print_verbose(&tmp_cache, print_event_info);
-        close_cached_file(&tmp_cache);
-        if (unlikely(error))
+        Dynamic_array<Rows_log_event *> &evs= print_event_info->verbose_events;
+        for (size_t i= 0; i < evs.elements(); ++i)
         {
-          delete ev;
-          goto err;
+          if (unlikely(describe_event(file, print_event_info, evs.at(i))))
+          {
+            evs.clear();
+            goto err;
+          }
         }
+        evs.clear();
       }
-#else
-      if (print_event_info->verbose)
-      {
-        /*
-          Verbose event printout can't start before encoded data
-          got enquoted. This is done at this point though multi-row
-          statement remain vulnerable.
-          TODO: fix MDEV-10362 to remove this workaround.
-        */
-        if (print_event_info->base64_output_mode !=
-            BASE64_OUTPUT_DECODE_ROWS)
-          my_b_printf(file, "'%s\n", print_event_info->delimiter);
-        error= ev->print_verbose(file, print_event_info);
-      }
-      else
-      {
-        ev->count_row_events(print_event_info);
-      }
-#endif
-      delete ev;
-      if (unlikely(error))
+      if (ev && unlikely(describe_event(file, print_event_info, ev)))
         goto err;
     }
   }
@@ -3916,6 +3890,62 @@ bool Log_event::print_base64(IO_CACHE* file,
 }
 
 
+static
+bool describe_event(IO_CACHE* file, PRINT_EVENT_INFO* print_event_info,
+                    Rows_log_event *ev)
+{
+  bool error= 0;
+
+#ifdef WHEN_FLASHBACK_REVIEW_READY
+  ev->need_flashback_review= need_flashback_review;
+  if (print_event_info->verbose)
+  {
+    if (ev->print_verbose(file, print_event_info))
+      goto err;
+  }
+  else
+  {
+    IO_CACHE tmp_cache;
+
+    if (open_cached_file(&tmp_cache, NULL, NULL, 0,
+                          MYF(MY_WME | MY_NABP)))
+    {
+      delete ev;
+      goto err;
+    }
+
+    error= ev->print_verbose(&tmp_cache, print_event_info);
+    close_cached_file(&tmp_cache);
+    if (unlikely(error))
+    {
+      delete ev;
+      goto err;
+    }
+  }
+#else
+  if (print_event_info->verbose)
+  {
+    /*
+      Verbose event printout can't start before encoded data
+      got enquoted. This is done at this point though multi-row
+      statement remain vulnerable.
+      TODO: fix MDEV-10362 to remove this workaround.
+    */
+    if (print_event_info->base64_output_mode !=
+        BASE64_OUTPUT_DECODE_ROWS)
+      my_b_printf(file, "'%s\n", print_event_info->delimiter);
+    error= ev->print_verbose(file, print_event_info);
+  }
+  else
+  {
+    ev->count_row_events(print_event_info);
+  }
+#endif
+  delete ev;
+  return error;
+}
+
+
 /*
   Log_event::print_timestamp()
 */
@@ -6033,13 +6063,19 @@ bool Start_log_event_v3::print(FILE* file, PRINT_EVENT_INFO* print_event_info)
     bool do_print_encoded=
       print_event_info->base64_output_mode != BASE64_OUTPUT_DECODE_ROWS;
     if (do_print_encoded)
+    {
       my_b_printf(&cache, "BINLOG '\n");
+      print_event_info->inside_binlog= true;
+    }
 
     if (print_base64(&cache, print_event_info, do_print_encoded))
       goto err;
 
     if (do_print_encoded)
+    {
       my_b_printf(&cache, "'%s\n", print_event_info->delimiter);
+      print_event_info->inside_binlog= false;
+    }
 
     print_event_info->printed_fd_event= TRUE;
   }
@@ -14972,6 +15008,7 @@ st_print_event_info::st_print_event_info()
   short_form= false;
   skip_replication= 0;
   printed_fd_event=FALSE;
+  inside_binlog= false;
   file= 0;
   base64_output_mode=BASE64_OUTPUT_UNSPEC;
   open_cached_file(&head_cache, NULL, NULL, 0, flags);
diff --git a/sql/log_event.h b/sql/log_event.h
index 9f598012e8a..d5979a00256 100644
--- a/sql/log_event.h
+++ b/sql/log_event.h
@@ -51,6 +51,7 @@
 #endif
 
 #include "rpl_gtid.h"
+#include "sql_array.h"
 
 /* Forward declarations */
 #ifndef MYSQL_CLIENT
@@ -817,6 +818,8 @@ enum enum_base64_output_mode {
 
 bool copy_event_cache_to_string_and_reinit(IO_CACHE *cache, LEX_STRING *to);
 
+class Rows_log_event;
+
 /*
   A structure for mysqlbinlog to know how to print events
 
@@ -881,6 +884,8 @@ typedef struct st_print_event_info
     statement for it.
   */
   bool skip_replication;
+  bool inside_binlog;
+  Dynamic_array<Rows_log_event *> verbose_events;
 
   /*
      These two caches are used by the row-based replication events to
