zed-industries/zed · error

already subscribed to entity

Error message

already subscribed to entity

What it means

subscribe_to_entity (crates/rpc/src/proto_client.rs:625) records a weak Entity handle under the pair (TypeId::of::<E>(), remote_id) so incoming messages for that remote entity are delivered to the local entity. The map allows one subscriber per (type, remote id); subscribing the same pair twice panics. There is no manual unsubscribe — entries clear only when the previous weak handle dies, so a still-live prior entity blocks a second subscribe.

Source

Thrown at crates/rpc/src/proto_client.rs:627

                .payload
                .remote_entity_id()
        };
        self.0
            .client
            .message_handler_set()
            .lock()
            .add_entity_message_handler(
                message_type_id,
                entity_type_id,
                entity_id_extractor,
                Arc::new(move |entity, envelope, _, cx| {
                    let entity = entity.downcast::<E>().unwrap();
                    let envelope = envelope.into_any().downcast::<TypedEnvelope<M>>().unwrap();
                    handler(entity, *envelope, cx).boxed_local()
                }),
            );
    }

    pub fn subscribe_to_entity<E: 'static>(&self, remote_id: u64, entity: &Entity<E>) {
        let id = (TypeId::of::<E>(), remote_id);

        let mut message_handlers = self.0.client.message_handler_set().lock();
        if message_handlers
            .entities_by_type_and_remote_id
            .contains_key(&id)
        {
            panic!("already subscribed to entity");
        }

        message_handlers.entities_by_type_and_remote_id.insert(
            id,
            EntityMessageSubscriber::Entity {
                handle: entity.downgrade().into(),
            },
        );
    }

View on GitHub (pinned to 9d272b0363)

Solutions

  1. Make the follow path idempotent at your layer: track (TypeId, remote_id) pairs you subscribed and reuse the existing entity instead of subscribing again.
  2. Ensure the old Entity and anything holding handles to it are dropped before subscribing a replacement; look for detached tasks or caches.
  3. Use the panic backtrace to find which follow flow runs twice and gate it.
  4. In tests, create fresh remote ids or drop prior entities before re-subscribing.

Example fix

// before
client.subscribe_to_entity(remote_id, &new_entity); // old entity still alive

// after: drop the previous handle before resubscribing
drop(old_entity);
client.subscribe_to_entity(remote_id, &new_entity);
Defensive patterns

Strategy: validation

Validate before calling

use std::any::TypeId;

// keep your own bookkeeping of live subscriptions:
if !self.subscribed.contains(&(TypeId::of::<E>(), remote_id)) {
    self.client.subscribe_to_entity(remote_id, &entity);
    self.subscribed.insert((TypeId::of::<E>(), remote_id));
} else {
    // reuse the existing entity instead of re-subscribing
}

Prevention

When it happens

Trigger: Calling client.subscribe_to_entity(remote_id, &entity) for the same entity type and remote id while the previously subscribed handle is still alive — following the same remote participant/entity twice, or recreating a local entity for a remote id whose old Entity has not been dropped yet.

Common situations: Re-follow/unfollow races in collaboration; quickly re-opening a shared pane or entity view; a detached task or cache holding an Entity handle that keeps the first subscriber alive.

Related errors


AI-assisted analysis of zed-industries/zed@9d272b0363 (2026-08-20). Data as JSON: /api/errors/78b958afdae12952. Report an issue: GitHub.