zed-industries/zed · warning

user was already invited

Error message

user was already invited

What it means

ActiveCall::invite de-duplicates concurrent invites with a pending_invites set; inserting a called_user_id that is already pending returns this error immediately from a ready task, without contacting the server.

Source

Thrown at crates/call/src/call_impl/mod.rs:493

    }

    pub fn global(cx: &App) -> Entity<Self> {
        Self::try_global(cx).unwrap()
    }

    pub fn try_global(cx: &App) -> Option<Entity<Self>> {
        let any = cx.try_global::<GlobalAnyActiveCall>()?;
        any.0.entity().downcast::<Self>().ok()
    }

    pub fn invite(
        &mut self,
        called_user_id: u64,
        initial_project: Option<Entity<Project>>,
        cx: &mut Context<Self>,
    ) -> Task<Result<()>> {
        if !self.pending_invites.insert(called_user_id) {
            return Task::ready(Err(anyhow!("user was already invited")));
        }
        cx.notify();

        if self._join_debouncer.running() {
            return Task::ready(Ok(()));
        }

        let room = if let Some(room) = self.room().cloned() {
            Some(Task::ready(Ok(room)).shared())
        } else {
            self.pending_room_creation.clone()
        };

        let invite = if let Some(room) = room {
            cx.spawn(async move |_, cx| {
                let room = room.await.map_err(|err| anyhow!("{err:?}"))?;

                let initial_project_id = if let Some(initial_project) = initial_project {

View on GitHub (pinned to bc538def45)

Solutions

  1. Await (or track) the first invite task before issuing another for the same user.
  2. Disable or debounce the invite button while an invite is pending.
  3. Treat the error as an idempotent no-op when the intent was a duplicate retry.

Example fix

// before: fire-and-forget, duplicate clicks double-invite
call.update(cx, |c, cx| c.invite(user_id, project, cx));

// after: track in-flight invites and short-circuit duplicates
if invite_in_flight.contains(&user_id) {
    return Task::ready(Ok(()));
}
invite_in_flight.insert(user_id);
call.update(cx, |c, cx| c.invite(user_id, project, cx))
Defensive patterns

Strategy: validation

Validate before calling

if invite_in_flight.contains(&called_user_id) {
    return Task::ready(Ok(())); // idempotent no-op
}
invite_in_flight.insert(called_user_id);
let task = call.update(cx, |call, cx| call.invite(called_user_id, project, cx));
// remove from invite_in_flight when the task completes

Try / catch

match invite-task errors containing "user was already invited" and map them to Ok(()) - the invite is already in flight, so treat it as success in the UI.

Prevention

When it happens

Trigger: Calling invite(user_id, ...) twice before the first invite task completes - double-click on a call button, duplicated UI events, or programmatic retries that do not await the first Task.

Common situations: UI buttons firing twice; retry wrappers re-invoking invite without awaiting; rapid successive calls to the same person.

Related errors


AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16). Data as JSON: /api/errors/5b71e8da2e431e75. Report an issue: GitHub.