diff --git src/bp-blogs/bp-blogs-activity.php src/bp-blogs/bp-blogs-activity.php
index c52287c..030507f 100644
--- src/bp-blogs/bp-blogs-activity.php
+++ src/bp-blogs/bp-blogs-activity.php
@@ -117,35 +117,68 @@ function bp_blogs_format_activity_action_new_blog_post( $action, $activity ) {
 		bp_blogs_update_blogmeta( $activity->item_id, 'name', $blog_name );
 	}
 
-	if ( empty( $activity->post_url ) ) {
-		$post_url = add_query_arg( 'p', $activity->secondary_item_id, trailingslashit( $blog_url ) );
-	} else {
+	/**
+	 * When the post is published we are faking an activity object
+	 * to which we add 2 properties :
+	 * - the post url
+	 * - the post title
+	 * This is done to build the 'post link' part of the activity
+	 * action string.
+	 * NB: in this case the activity has not yet been created.
+	 */
+	if ( isset( $activity->post_url ) ) {
 		$post_url = $activity->post_url;
-	}
 
-	if ( empty( $activity->post_title ) ) {
-		$post_title = bp_activity_get_meta( $activity->id, 'post_title' );
+	/**
+	 * The post_url property is not set, we need to build the url
+	 * thanks to the post id which is also saved as the secondary
+	 * item id property of the activity object.
+	 */
 	} else {
+		$post_url = add_query_arg( 'p', $activity->secondary_item_id, trailingslashit( $blog_url ) );
+	}
+
+	// Should be the case when the post has just been published
+	if ( isset( $activity->post_title ) ) {
 		$post_title = $activity->post_title;
+
+	// If activity already exists try to get the post title from activity meta
+	} else if ( ! empty( $activity->id ) ) {
+		$post_title = bp_activity_get_meta( $activity->id, 'post_title' );
 	}
 
-	// Should only be empty at the time of post creation
+	/**
+	 * In case the post was published without a title
+	 * or the activity meta was not found
+	 */
 	if ( empty( $post_title ) ) {
+		// Defaults to no title
+		$post_title = esc_html__( '(no title)', 'buddypress' );
+
 		switch_to_blog( $activity->item_id );
 
 		$post = get_post( $activity->secondary_item_id );
 		if ( is_a( $post, 'WP_Post' ) ) {
-			$post_title = $post->post_title;
-			bp_activity_update_meta( $activity->id, 'post_title', $post_title );
+			// Does the post have a title ?
+			if ( ! empty( $post->post_title ) ) {
+				$post_title = $post->post_title;
+			}
+
+			// Make sure the activity exists before saving the post title in activity meta
+			if ( ! empty( $activity->id ) ) {
+				bp_activity_update_meta( $activity->id, 'post_title', $post_title );
+			}
 		}
 
 		restore_current_blog();
 	}
 
+	// Build the 'post link' part of the activity action string
 	$post_link  = '<a href="' . $post_url . '">' . $post_title . '</a>';
 
 	$user_link = bp_core_get_userlink( $activity->user_id );
 
+	// Build the complete activity action string
 	if ( is_multisite() ) {
 		$action  = sprintf( __( '%1$s wrote a new post, %2$s, on the site %3$s', 'buddypress' ), $user_link, $post_link, '<a href="' . esc_url( $blog_url ) . '">' . esc_html( $blog_name ) . '</a>' );
 	} else {
diff --git tests/phpunit/testcases/blogs/activity.php tests/phpunit/testcases/blogs/activity.php
index e67adab..6eed606 100644
--- tests/phpunit/testcases/blogs/activity.php
+++ tests/phpunit/testcases/blogs/activity.php
@@ -283,6 +283,95 @@ class BP_Tests_Blogs_Activity extends BP_UnitTestCase {
 
 		$this->assertEquals( $this->comment_post_id, $p );
 	}
+
+	/**
+	 * @group activity_action
+	 * @group bp_blogs_format_activity_action_new_blog_post
+	 */
+	public function test_bp_blogs_format_activity_action_new_blog_post_no_title() {
+		if ( is_multisite() ) {
+			return;
+		}
+
+		$u = $this->factory->user->create();
+		$p = wp_insert_post( array(
+			'post_author' => $u,
+			'post_title'  => '', // no title: the object of the test
+			'post_status' => 'publish',
+			'post_content' => 'foo bar',
+		) );
+
+		$user_link = bp_core_get_userlink( $u );
+		$blog_url = get_home_url();
+		$post_url = add_query_arg( 'p', $p, trailingslashit( $blog_url ) );
+		$post_link = '<a href="' . $post_url . '">(no title)</a>';
+
+		// Set actions
+		bp_activity_get_actions();
+
+		$a_obj = bp_activity_get( array(
+			'item_id'           => 1,
+			'secondary_item_id' => $p,
+		) );
+
+		$expected = sprintf( '%s wrote a new post, %s', $user_link, $post_link );
+
+		$this->assertSame( $expected, $a_obj['activities'][0]->action );
+	}
+
+	/**
+	 * @group activity_action
+	 * @group bp_blogs_format_activity_action_new_blog_post
+	 */
+	public function test_bp_blogs_format_activity_action_new_blog_post_updated_without_title() {
+		if ( is_multisite() ) {
+			return;
+		}
+
+		$u = $this->factory->user->create();
+		$p = wp_insert_post( array(
+			'post_author' => $u,
+			'post_title'  => 'foo',
+			'post_status' => 'publish',
+			'post_content' => 'foo bar',
+		) );
+
+		$user_link  = bp_core_get_userlink( $u );
+		$blog_url   = get_home_url();
+		$post_url   = add_query_arg( 'p', $p, trailingslashit( $blog_url ) );
+		$post_title = get_the_title( $p );
+		$post_link  = '<a href="' . $post_url . '">' . $post_title . '</a>';
+
+		// Set actions
+		bp_activity_get_actions();
+
+		$a_obj = bp_activity_get( array(
+			'item_id'           => 1,
+			'secondary_item_id' => $p,
+		) );
+
+		$expected = sprintf( '%s wrote a new post, %s', $user_link, $post_link );
+
+		$this->assertSame( $expected, $a_obj['activities'][0]->action );
+
+		// Update the post by removing its title
+		wp_update_post( array(
+			'ID'         => $p,
+			'post_title' => '',
+		) );
+
+		// we now expect the (no title) post link
+		$post_link = '<a href="' . $post_url . '">(no title)</a>';
+		$expected = sprintf( '%s wrote a new post, %s', $user_link, $post_link );
+
+		$a_obj = bp_activity_get( array(
+			'item_id'           => 1,
+			'secondary_item_id' => $p,
+		) );
+
+		$this->assertSame( $expected, $a_obj['activities'][0]->action );
+	}
+
 	/**
 	 * Dopey passthrough method so we can check that the correct values
 	 * are being passed to the filter
