LemmyNet/lemmy · error
err getting actor {actor_type:?} {actor_apub_id}: {e:?}
Error message
err getting actor {actor_type:?} {actor_apub_id}: {e:?} What it means
get_actor_cached wraps any failure while loading an actor (person/community/etc.) from the DB by its ActivityPub ID into this anyhow error. It uses the underlying LemmyError as context, so the root cause is in the debug-printed inner error. send_retry_loop calls it before serializing the activity.
Source
Thrown at crates/apub/send/src/util.rs:174
.into(),
)),
ActorType::Person => Right(Left(
Person::read_from_apub_id(pool, &url)
.await?
.context("apub person not found")?
.into(),
)),
ActorType::MultiCommunity => Left(Right(
MultiCommunity::read_from_apub_id(pool, &url)
.await?
.context("apub multi-comm not found")?
.into(),
)),
};
Result::<_, LemmyError>::Ok(Arc::new(actor))
})
.await
.map_err(|e| anyhow::anyhow!("err getting actor {actor_type:?} {actor_apub_id}: {e:?}"))
}
type CachedActivityInfo = Option<Arc<SentActivity>>;
/// activities are immutable so cache does not need to have TTL
/// May return None if the corresponding id does not exist or is a received activity.
/// Holes in serials are expected behaviour in postgresql
/// todo: cache size should probably be configurable / dependent on desired memory usage
pub(crate) async fn get_activity_cached(
pool: &mut DbPool<'_>,
activity_id: ActivityId,
) -> Result<CachedActivityInfo> {
static ACTIVITIES: LazyLock<Cache<ActivityId, CachedActivityInfo>> =
LazyLock::new(|| Cache::builder().max_capacity(10000).build());
ACTIVITIES
.try_get_with(activity_id, async {
Ok(Some(Arc::new(SentActivity::read(pool, activity_id).await?)))
})
.awaitView on GitHub (pinned to 439734dd63)
Solutions
- Check the inner debug error to see whether it is not-found vs a DB failure
- Verify the actor still exists and is not marked deleted/removed in the database
- Confirm activity.actor_type matches the actual actor kind at actor_apub_id
- If DB-related, check database connectivity/health and retry the federation
Example fix
// before
let actor = get_actor_cached(pool, activity.actor_type, &actor_apub_id).await?; // fails for removed actor
// after
let actor = match get_actor_cached(pool, activity.actor_type, &actor_apub_id).await {
Ok(a) => a,
Err(e) => { warn!("actor gone, dropping activity: {e:?}"); continue; } // skip rather than retry forever
}; Defensive patterns
Strategy: try-catch
Validate before calling
// before sending, check the actor row exists // SELECT 1 FROM actor WHERE ap_id = $1 AND deleted = false AND removed = false
Try / catch
match get_actor_cached(pool, activity.actor_type, &ap_id).await {
Ok(actor) => send_with(actor),
Err(e) => warn!("actor unavailable, dropping activity: {e:?}"),
} Prevention
- Skip (don't infinite-retry) activities whose actor was deleted/removed
- Keep actor_type and ap_id consistent when constructing activities
- Monitor DB health; actor lookups are DB-bound
When it happens
Trigger: Federation send for an activity whose actor_type + actor_apub_id does not resolve to a live actor row: deleted/removed actor, actor purged from DB, wrong actor_type, or an underlying DB query failure.
Common situations: Trying to federate an activity authored by a user or community that has since been deleted or removed by moderators; DB connectivity issues on the instance; stale cache entries pointing at removed actors.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- activity is from before lemmy 0.19
- err getting activity: {e:?}
- err getting id: {e:?}
- {}: next id to send is not as expected: {:?} != {:?}
- err getting local site: {e:?}
AI-assisted analysis of LemmyNet/lemmy@439734dd63 (2026-09-06).
Data as JSON: /api/errors/3de512c9f4433841.
Report an issue: GitHub.