Repository: LemmyNet/lemmy (language: rust)

Issue:
[Bug]: {"error":"couldnt_find_person"} for some community outboxes
### Requirements

- [x] Is this a bug report? For questions or discussions use https://lemmy.ml/c/lemmy_support
- [x] Did you check to see if this issue already exists?
- [x] Is this only a single bug? Do not put multiple bugs in one issue.
- [x] Do you agree to follow the rules in our [Code of Conduct](https://join-lemmy.org/docs/code_of_conduct.html)?
- [x] Is this a backend issue? Use the [lemmy-ui](https://github.com/LemmyNet/lemmy-ui) repo for UI / frontend issues.

### Summary

Some communities, that should return a full outbox response are instead returning `{"error":"couldnt_find_person"}`. This is similar to #4658, but the error message is different, and I think that was fixed in 0.19.4, but the relevant instances are on later versions.

### Steps to Reproduce

1. `curl -H 'accept: application/activity+json' -L https://sh.itjust.works/c/linkedinlunatics/outbox | jq .`
2. `curl -H 'accept: application/activity+json' -L https://lemmy.dbzer0.com/c/yepowertrippinbastards/outbox | jq .`
3. See {"error": "couldnt_find_person"} response



### Technical Details

https://sh.itjust.works/ is on 0.19.5, and https://lemmy.dbzer0.com is on 0.19.8

### Version

BE 0.19.8

### Lemmy Instance URL

_No response_


Discussion / hints:
Can confirm, it seems to be to still be about handling of deleted accounts as the last 50 posts for c/linkedinlunatics includes `https://lemmy.world/post/17014995` which is by a deleted account.

Fix patch:
diff --git a/crates/apub/src/collections/community_outbox.rs b/crates/apub/src/collections/community_outbox.rs
index c015dc591c..3cab387ff5 100644
--- a/crates/apub/src/collections/community_outbox.rs
+++ b/crates/apub/src/collections/community_outbox.rs
@@ -47,17 +47,21 @@ impl Collection for ApubCommunityOutbox {
 
     let mut ordered_items = vec![];
     for post_view in post_views {
-      let create = CreateOrUpdatePage::new(
+      // ignore errors, in particular if post creator was deleted
+      if let Ok(create) = CreateOrUpdatePage::new(
         post_view.post.into(),
         &post_view.creator.into(),
         owner,
         CreateOrUpdateType::Create,
         data,
       )
-      .await?;
-      let announcable = AnnouncableActivities::CreateOrUpdatePost(create);
-      let announce = AnnounceActivity::new(announcable.try_into()?, owner, data)?;
-      ordered_items.push(announce);
+      .await
+      {
+        let announcable = AnnouncableActivities::CreateOrUpdatePost(create);
+        if let Ok(announce) = AnnounceActivity::new(announcable.try_into()?, owner, data) {
+          ordered_items.push(announce);
+        }
+      }
     }
 
     Ok(GroupOutbox {
diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs
index 8a6785a41a..7121b453e2 100644
--- a/crates/apub/src/http/community.rs
+++ b/crates/apub/src/http/community.rs
@@ -168,6 +168,8 @@ pub(crate) mod tests {
       instance::Instance,
       local_site::{LocalSite, LocalSiteInsertForm},
       local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitInsertForm},
+      person::{Person, PersonInsertForm},
+      post::{Post, PostInsertForm},
       site::{Site, SiteInsertForm},
     },
     traits::Crud,
@@ -180,7 +182,7 @@ pub(crate) mod tests {
     deleted: bool,
     visibility: CommunityVisibility,
     context: &Data<LemmyContext>,
-  ) -> LemmyResult<(Instance, Community)> {
+  ) -> LemmyResult<(Instance, Community, Path<CommunityPath>)> {
     let instance =
       Instance::read_or_create(&mut context.pool(), "my_domain.tld".to_string()).await?;
     create_local_site(context, instance.id).await?;
@@ -196,7 +198,11 @@ pub(crate) mod tests {
       )
     };
     let community = Community::create(&mut context.pool(), &community_form).await?;
-    Ok((instance, community))
+    let path: Path<CommunityPath> = CommunityPath {
+      community_name: community.name.clone(),
+    }
+    .into();
+    Ok((instance, community, path))
   }
 
   /// Necessary for the community outbox fetching
@@ -226,7 +232,7 @@ pub(crate) mod tests {
   #[serial]
   async fn test_get_community() -> LemmyResult<()> {
     let context = LemmyContext::init_test_context().await;
-    let (instance, community) = init(false, CommunityVisibility::Public, &context).await?;
+    let (instance, community, path) = init(false, CommunityVisibility::Public, &context).await?;
     let request = TestRequest::default().to_http_request();
 
     // fetch invalid community
@@ -237,9 +243,6 @@ pub(crate) mod tests {
     assert!(res.is_err());
 
     // fetch valid community
-    let path = CommunityPath {
-      community_name: community.name.clone(),
-    };
     let res = get_apub_community_http(path.clone().into(), context.reset_request_count()).await?;
     assert_eq!(200, res.status());
     let res_group: Group = decode_response(res).await?;
@@ -266,8 +269,7 @@ pub(crate) mod tests {
     let res =
       get_apub_community_moderators(path.clone().into(), context.reset_request_count()).await?;
     assert_eq!(200, res.status());
-    let res =
-      get_apub_community_outbox(path.into(), context.reset_request_count(), request).await?;
+    let res = get_apub_community_outbox(path, context.reset_request_count(), request).await?;
     assert_eq!(200, res.status());
 
     Instance::delete(&mut context.pool(), instance.id).await?;
@@ -278,14 +280,10 @@ pub(crate) mod tests {
   #[serial]
   async fn test_get_deleted_community() -> LemmyResult<()> {
     let context = LemmyContext::init_test_context().await;
-    let (instance, community) = init(true, CommunityVisibility::LocalOnly, &context).await?;
+    let (instance, _, path) = init(true, CommunityVisibility::LocalOnly, &context).await?;
     let request = TestRequest::default().to_http_request();
 
     // should return tombstone
-    let path: Path<CommunityPath> = CommunityPath {
-      community_name: community.name.clone(),
-    }
-    .into();
     let res = get_apub_community_http(path.clone().into(), context.reset_request_count()).await?;
     assert_eq!(410, res.status());
     let res_tombstone = decode_response::<Tombstone>(res).await;
@@ -322,13 +320,9 @@ pub(crate) mod tests {
   #[serial]
   async fn test_get_local_only_community() -> LemmyResult<()> {
     let context = LemmyContext::init_test_context().await;
-    let (instance, community) = init(false, CommunityVisibility::LocalOnly, &context).await?;
+    let (instance, _, path) = init(false, CommunityVisibility::LocalOnly, &context).await?;
     let request = TestRequest::default().to_http_request();
 
-    let path: Path<CommunityPath> = CommunityPath {
-      community_name: community.name.clone(),
-    }
-    .into();
     let res = get_apub_community_http(path.clone().into(), context.reset_request_count()).await;
     assert!(res.is_err());
     let res = get_apub_community_featured(
@@ -356,4 +350,26 @@ pub(crate) mod tests {
     Instance::delete(&mut context.pool(), instance.id).await?;
     Ok(())
   }
+
+  #[tokio::test]
+  #[serial]
+  async fn test_outbox_deleted_user() -> LemmyResult<()> {
+    let context = LemmyContext::init_test_context().await;
+    let (instance, community, path) = init(false, CommunityVisibility::Public, &context).await?;
+    let request = TestRequest::default().to_http_request();
+
+    // post from deleted user shouldnt break outbox
+    let mut form = PersonInsertForm::new("jerry".to_string(), String::new(), instance.id);
+    form.deleted = Some(true);
+    let person = Person::create(&mut context.pool(), &form).await?;
+
+    let form = PostInsertForm::new("title".to_string(), person.id, community.id);
+    Post::create(&mut context.pool(), &form).await?;
+
+    let res = get_apub_community_outbox(path, context.reset_request_count(), request).await?;
+    assert_eq!(200, res.status());
+
+    Instance::delete(&mut context.pool(), instance.id).await?;
+    Ok(())
+  }
 }


Test patch: