Index: wp-includes/comment.php
===================================================================
--- wp-includes/comment.php	(revision 30551)
+++ wp-includes/comment.php	(working copy)
@@ -1095,11 +1095,12 @@
  *
  * @global wpdb $wpdb WordPress database abstraction object.
  *
- * @param array $commentdata Contains information on the comment
- * @return mixed Signifies the approval status (0|1|'spam')
+ * @param array $commentdata Contains information on the comment.
+ * @param bool $wp_error Whether to return a WP_Error object instead of dying if there is a failure. Default is false.
+ * @return mixed Signifies the approval status (0|1|'spam') on success, optionally WP_Error on failure.
  */
-function wp_allow_comment( $commentdata ) {
-	global $wpdb;
+function wp_allow_comment( $commentdata, $wp_error = false ) {
+	global $wpdb, $_comment_flood_wp_error_flag, $_comment_flood_wp_error_obj;
 
 	// Simple duplicate check
 	// expected_slashed ($comment_post_ID, $comment_author, $comment_author_email, $comment_content)
@@ -1128,12 +1129,22 @@
 		 * @param array $commentdata Comment data.
 		 */
 		do_action( 'comment_duplicate_trigger', $commentdata );
-		if ( defined( 'DOING_AJAX' ) ) {
-			die( __('Duplicate comment detected; it looks as though you&#8217;ve already said that!') );
+		$msg = __( 'Duplicate comment detected; it looks as though you&#8217;ve already said that!' );
+		if ( $wp_error ) {
+			return new WP_Error( 'duplicate_comment', $msg );
+		} elseif ( defined( 'DOING_AJAX' ) ) {
+			die( $msg );
+		} else {
+			wp_die( $msg );
 		}
-		wp_die( __('Duplicate comment detected; it looks as though you&#8217;ve already said that!') );
 	}
 
+	$old_cf_error_flag = $_comment_flood_wp_error_flag;
+	$_comment_flood_wp_error_flag = $wp_error;
+	if ( $wp_error ) {
+		$_comment_flood_wp_error_obj = null;
+	}
+
 	/**
 	 * Fires immediately before a comment is marked approved.
 	 *
@@ -1152,6 +1163,12 @@
 		$commentdata['comment_date_gmt']
 	);
 
+	$_comment_flood_wp_error_flag = $old_cf_error_flag;
+
+	if ( $wp_error && is_wp_error( $_comment_flood_wp_error_obj ) ) {
+		return $_comment_flood_wp_error_obj;
+	}
+
 	if ( ! empty( $commentdata['user_id'] ) ) {
 		$user = get_userdata( $commentdata['user_id'] );
 		$post_author = $wpdb->get_var( $wpdb->prepare(
@@ -1203,7 +1220,19 @@
 	return $approved;
 }
 
+// Comment flood detection error handling
+
 /**
+ * Whether check_comment_flood_db() should die or return a WP_Error object if a comment flood is detected.
+ * @global bool $_comment_flood_wp_error_flag
+ *
+ * The WP_Error object returned by check_comment_flood_db().
+ * @global WP_Error $_comment_flood_wp_error_obj
+ */
+global $_comment_flood_wp_error_flag, $_comment_flood_wp_error_obj;
+$_comment_flood_wp_error_flag = false;
+
+/**
  * Check whether comment flooding is occurring.
  *
  * Won't run, if current user can manage options, so to not block
@@ -1218,7 +1247,10 @@
  * @param string $date MySQL time string.
  */
 function check_comment_flood_db( $ip, $email, $date ) {
-	global $wpdb;
+	global $wpdb, $_comment_flood_wp_error_flag, $_comment_flood_wp_error_obj;
+	if ( $_comment_flood_wp_error_flag ) {
+		$_comment_flood_wp_error_obj = null;
+	}
 	if ( current_user_can( 'manage_options' ) )
 		return; // don't throttle admins
 	$hour_ago = gmdate( 'Y-m-d H:i:s', time() - HOUR_IN_SECONDS );
@@ -1246,10 +1278,15 @@
 			 */
 			do_action( 'comment_flood_trigger', $time_lastcomment, $time_newcomment );
 
-			if ( defined('DOING_AJAX') )
-				die( __('You are posting comments too quickly. Slow down.') );
-
-			wp_die( __('You are posting comments too quickly. Slow down.'), '', array('response' => 403) );
+			$msg = __('You are posting comments too quickly. Slow down.');
+			if ( $_comment_flood_wp_error_flag ) {
+				$_comment_flood_wp_error_obj = new WP_Error( 'comment_flood', $msg );
+				return;
+			} elseif ( defined( 'DOING_AJAX' ) ) {
+				die( $msg );
+			} else {
+				wp_die( $msg, '', array('response' => 403) );
+			}
 		}
 	}
 }
@@ -2041,9 +2078,10 @@
  *
  * @since 1.5.0
  * @param array $commentdata Contains information on the comment.
- * @return int|bool The ID of the comment on success, false on failure.
+ * @param bool $wp_error Whether to return a WP_Error object if there is a failure. Default is false.
+ * @return int|bool|WP_Error The ID of the comment on success, false or WP_Error on failure.
  */
-function wp_new_comment( $commentdata ) {
+function wp_new_comment( $commentdata, $wp_error = false ) {
 	if ( isset( $commentdata['user_ID'] ) ) {
 		$commentdata['user_id'] = $commentdata['user_ID'] = (int) $commentdata['user_ID'];
 	}
@@ -2078,7 +2116,12 @@
 
 	$commentdata = wp_filter_comment($commentdata);
 
-	$commentdata['comment_approved'] = wp_allow_comment($commentdata);
+	$approved = wp_allow_comment( $commentdata, $wp_error );
+	if ( $wp_error && is_wp_error( $approved ) ) {
+		return $approved;
+	} else {
+		$commentdata['comment_approved'] = $approved;
+	}
 
 	$comment_ID = wp_insert_comment($commentdata);
 	if ( ! $comment_ID ) {
