Index: src/wp-admin/includes/ajax-actions.php
===================================================================
--- src/wp-admin/includes/ajax-actions.php	(revision 38168)
+++ src/wp-admin/includes/ajax-actions.php	(working copy)
@@ -3594,7 +3594,8 @@
 
 	$status['pluginName'] = $api->name;
 
-	$upgrader = new Plugin_Upgrader( new Automatic_Upgrader_Skin() );
+	$skin     = new WP_Ajax_Upgrader_Skin();
+	$upgrader = new Plugin_Upgrader( );
 	$result   = $upgrader->install( $api->download_link );
 
 	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
@@ -3604,6 +3605,23 @@
 	if ( is_wp_error( $result ) ) {
 		$status['errorMessage'] = $result->get_error_message();
 		wp_send_json_error( $status );
+	} elseif ( is_wp_error( $skin->result ) ) {
+		$status['errorMessage'] = $skin->result->get_error_message();
+		wp_send_json_error( $status );
+	} elseif ( $skin->get_errors()->get_error_code() ) {
+		$errors = $skin->get_errors();
+
+		$messages = array();
+		foreach ( $errors->get_error_codes() as $error_code ) {
+			if ( $errors->get_error_data( $error_code ) && is_string( $errors->get_error_data( $error_code ) ) ) {
+				$messages[] = $errors->get_error_message( $error_code ) . ' ' . esc_html( strip_tags( $errors->get_error_data( $error_code ) ) );
+			} else {
+				$messages[] = $errors->get_error_message( $error_code );
+			}
+		}
+
+		$status['errorMessage'] = implode( ', ', $messages );
+		wp_send_json_error( $status );
 	} elseif ( is_null( $result ) ) {
 		global $wp_filesystem;
 
@@ -3612,6 +3630,7 @@
 
 		// Pass through the error from WP_Filesystem if one was raised.
 		if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
+			$status['errorCode']    = $wp_filesystem->errors->get_error_code();
 			$status['errorMessage'] = esc_html( $wp_filesystem->errors->get_error_message() );
 		}
 
@@ -3680,19 +3699,35 @@
 
 	wp_update_plugins();
 
-	$skin     = new Automatic_Upgrader_Skin();
+	$skin     = new WP_Ajax_Upgrader_Skin();
 	$upgrader = new Plugin_Upgrader( $skin );
 	$result   = $upgrader->bulk_upgrade( array( $plugin ) );
 
 	if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
-		$status['debug'] = $upgrader->skin->get_upgrade_messages();
+		$status['debug'] = $skin->get_upgrade_messages();
 	}
 
-	if ( is_array( $result ) && empty( $result[ $plugin ] ) && is_wp_error( $skin->result ) ) {
-		$result = $skin->result;
-	}
+	if ( is_wp_error( $result ) ) {
+		$status['errorMessage'] = $result->get_error_message();
+		wp_send_json_error( $status );
+	} elseif ( is_wp_error( $skin->result ) ) {
+		$status['errorMessage'] = $skin->result->get_error_message();
+		wp_send_json_error( $status );
+	} elseif ( $skin->get_errors()->get_error_code() ) {
+		$errors = $skin->get_errors();
 
-	if ( is_array( $result ) && ! empty( $result[ $plugin ] ) ) {
+		$messages = array();
+		foreach ( $errors->get_error_codes() as $error_code ) {
+			if ( $errors->get_error_data( $error_code ) && is_string( $errors->get_error_data( $error_code ) ) ) {
+				$messages[] = $errors->get_error_message( $error_code ) . ' ' . esc_html( strip_tags( $errors->get_error_data( $error_code ) ) );
+			} else {
+				$messages[] = $errors->get_error_message( $error_code );
+			}
+		}
+
+		$status['errorMessage'] = implode( ', ', $messages );
+		wp_send_json_error( $status );
+	} elseif ( is_array( $result ) && ! empty( $result[ $plugin ] ) ) {
 		$plugin_update_data = current( $result );
 
 		/*
@@ -3716,9 +3751,6 @@
 			$status['newVersion'] = sprintf( __( 'Version %s' ), $plugin_data['Version'] );
 		}
 		wp_send_json_success( $status );
-	} elseif ( is_wp_error( $result ) ) {
-		$status['errorMessage'] = $result->get_error_message();
-		wp_send_json_error( $status );
 	} elseif ( false === $result ) {
 		global $wp_filesystem;
 
Index: src/wp-admin/includes/class-wp-ajax-upgrader-skin.php
===================================================================
--- src/wp-admin/includes/class-wp-ajax-upgrader-skin.php	(revision 0)
+++ src/wp-admin/includes/class-wp-ajax-upgrader-skin.php	(working copy)
@@ -0,0 +1,110 @@
+<?php
+/**
+ * Upgrader API: WP_Ajax_Upgrader_Skin class
+ *
+ * @package WordPress
+ * @subpackage Upgrader
+ * @since 4.6.0
+ */
+
+/**
+ * Upgrader Skin for Ajax WordPress Upgrades
+ *
+ * This skin is designed to be used for Ajax updates.
+ *
+ * @since 4.6.0
+ *
+ * @see Automatic_Upgrader_Skin
+ */
+class WP_Ajax_Upgrader_Skin extends Automatic_Upgrader_Skin {
+	/**
+	 * Holds the WP_Error object.
+	 *
+	 * @since 4.6.0
+	 * @access protected
+	 *
+	 * @var null|WP_Error
+	 */
+	protected $errors = null;
+
+	/**
+	 * Retrieves the list of errors.
+	 *
+	 * @since 4.6.0
+	 * @access public
+	 *
+	 * @param array $args
+	 */
+	public function __construct( $args = array() ) {
+		parent::__construct( $args );
+
+		$this->errors = new WP_Error();
+	}
+
+	/**
+	 * Retrieves the list of errors.
+	 *
+	 * @since 4.6.0
+	 * @access public
+	 *
+	 * @return WP_Error Errors during an upgrade.
+	 */
+	public function get_errors() {
+		return $this->errors;
+	}
+
+	/**
+	 * Stores a log entry for an error.
+	 *
+	 * @since 4.6.0
+	 * @access public
+	 *
+	 * @param string|WP_Error $errors
+	 */
+	public function error( $errors ) {
+		if ( is_string( $errors ) ) {
+			$string = $errors;
+			if ( ! empty( $this->upgrader->strings[ $string ] ) ) {
+				$string = $this->upgrader->strings[ $string ];
+			}
+
+			if ( false !== strpos( $string, '%' ) ) {
+				$args = func_get_args();
+				$args = array_splice( $args, 1 );
+				if ( ! empty( $args ) ) {
+					$string = vsprintf( $string, $args );
+				}
+			}
+
+			// Count existing errors to generate an unique error code.
+			$errors_count = count( $errors->get_error_codes() );
+			$this->errors->add( 'unknown_error_' . $errors_count + 1 , $string );
+		} elseif ( is_wp_error( $errors ) ) {
+			foreach ( $errors->get_error_codes() as $error_code ) {
+				$this->errors->add( $error_code, $errors->get_error_message( $error_code ), $errors->get_error_data( $error_code ) );
+			}
+		}
+
+		$args = func_get_args();
+		call_user_func_array( array( $this, 'parent::error' ), $args );
+	}
+
+	/**
+	 * Stores a log entry.
+	 *
+	 * @since 4.6.0
+	 * @access public
+	 *
+	 * @param string|array|WP_Error $data
+	 */
+	public function feedback( $data ) {
+		if ( is_wp_error( $data ) ) {
+			foreach ( $data->get_error_codes() as $error_code ) {
+				$this->errors->add( $error_code, $data->get_error_message( $error_code ), $data->get_error_data( $error_code ) );
+			}
+		}
+
+		$args = func_get_args();
+		call_user_func_array( array( $this, 'parent::feedback' ), $args );
+	}
+}
Index: src/wp-admin/includes/class-wp-upgrader-skins.php
===================================================================
--- src/wp-admin/includes/class-wp-upgrader-skins.php	(revision 38167)
+++ src/wp-admin/includes/class-wp-upgrader-skins.php	(working copy)
@@ -36,3 +36,6 @@
 
 /** Automatic_Upgrader_Skin class */
 require_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php';
+
+/** WP_Ajax_Upgrader_Skin class */
+require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
Index: src/wp-admin/includes/class-wp-upgrader.php
===================================================================
--- src/wp-admin/includes/class-wp-upgrader.php	(revision 38167)
+++ src/wp-admin/includes/class-wp-upgrader.php	(working copy)
@@ -39,6 +39,9 @@
 /** Automatic_Upgrader_Skin class */
 require_once ABSPATH . 'wp-admin/includes/class-automatic-upgrader-skin.php';
 
+/** WP_Ajax_Upgrader_Skin class */
+require_once ABSPATH . 'wp-admin/includes/class-wp-ajax-upgrader-skin.php';
+
 /**
  * Core class used for upgrading/installing a local set of files via
  * the Filesystem Abstraction classes from a Zip file.
