Index: src/bp-messages/bp-messages-actions.php
===================================================================
--- src/bp-messages/bp-messages-actions.php
+++ src/bp-messages/bp-messages-actions.php
@@ -85,23 +85,24 @@
 			$recipients = apply_filters( 'bp_messages_recipients', $recipients );
 
 			// Attempt to send the message
-			$thread_id  = messages_new_message( array(
+			$send = messages_new_message( array(
 				'recipients' => $recipients,
 				'subject'    => $_POST['subject'],
-				'content'    => $_POST['content']
+				'content'    => $_POST['content'],
+				'error_type' => 'wp_error'
 			) );
 
 			// Send the message and redirect to it
-			if ( ! empty( $thread_id ) ) {
+			if ( true === is_int( $send ) ) {
 				$success     = true;
 				$feedback    = __( 'Message successfully sent.', 'buddypress' );
 				$view        = trailingslashit( $member_messages . 'view' );
-				$redirect_to = trailingslashit( $view . $thread_id );
+				$redirect_to = trailingslashit( $view . $send );
 
 			// Message could not be sent
 			} else {
 				$success  = false;
-				$feedback = __( 'Message was not sent. Please try again.', 'buddypress' );
+				$feedback = $send->get_error_message();
 			}
 		}
 	}
Index: src/bp-messages/bp-messages-functions.php
===================================================================
--- src/bp-messages/bp-messages-functions.php
+++ src/bp-messages/bp-messages-functions.php
@@ -32,6 +32,7 @@
  *                              threads, 'No Subject' will be used if no $subject is provided.
  *     @type string $content    Content of the message. Cannot be empty.
  *     @type string $date_sent  Date sent, in 'Y-m-d H:i:s' format. Default: current date/time.
+ *     @type string $error_type Optional. Error type. Either 'bool' or 'wp_error'. Default: 'bool'.
  * }
  * @return int|bool ID of the message thread on success, false on failure.
  */
@@ -44,12 +45,26 @@
 		'recipients' => array(), // Can be an array of usernames, user_ids or mixed.
 		'subject'    => false,
 		'content'    => false,
-		'date_sent'  => bp_core_current_time()
+		'date_sent'  => bp_core_current_time(),
+		'error_type' => 'bool'
 	), 'messages_new_message' );
 
 	// Bail if no sender or no content
 	if ( empty( $r['sender_id'] ) || empty( $r['content'] ) ) {
-		return false;
+		if ( 'wp_error' === $r['error_type'] ) {
+			if ( empty( $r['sender_id'] ) ) {
+				$error_code = 'messages_empty_sender';
+				$feedback = __( 'Your message was not sent. Please use a valid sender.', 'buddypress' );
+			} else {
+				$error_code = 'messages_empty_content';
+				$feedback = __( 'Your message was not sent. Please enter some content.', 'buddypress' );
+			}
+
+			return new WP_Error( $error_code, $feedback );
+
+		} else {
+			return false;
+		}
 	}
 
 	// Create a new message object
@@ -83,7 +98,11 @@
 
 		// Bail if no recipients
 		if ( empty( $r['recipients'] ) ) {
-			return false;
+			if ( 'wp_error' === $r['error_type'] ) {
+				return new WP_Error( 'message_empty_recipients', __( 'Message could not be sent. Please enter a recipient.', 'buddypress' ) );
+			} else {
+				return false;
+			}
 		}
 
 		// Set a default subject if none exists
@@ -139,7 +158,11 @@
 		// Remove duplicates & bail if no recipients
 		$recipient_ids = array_unique( $recipient_ids );
 		if ( empty( $recipient_ids ) ) {
-			return false;
+			if ( 'wp_error' === $r['error_type'] ) {
+				return new WP_Error( 'message_invalid_recipients', __( 'Message could not be sent because you have entered an invalid username. Please try again.', 'buddypress' ) );
+			} else {
+				return false;
+			}
 		}
 
 		// Format this to match existing recipients
@@ -150,6 +173,7 @@
 	}
 
 	// Bail if message failed to send
+	// @todo Should we use WP_Error higher up the stack in BP_Messages_Message::save()?
 	if ( ! $message->send() ) {
 		return false;
 	}
