zed-industries/zed · error
cannot call a user who isn't a contact
Error message
cannot call a user who isn't a contact
What it means
Thrown by the call_user RPC handler when has_contact(calling_user_id, called_user_id) is false. Zed only lets you ring users with whom you have an established (mutually accepted) contact relationship; the check runs before the room/call machinery is touched.
Source
Thrown at crates/collab/src/rpc.rs:1754
/// Call someone else into the current room
async fn call(
request: proto::Call,
response: Response<proto::Call>,
session: MessageContext,
) -> Result<()> {
let room_id = RoomId::from_proto(request.room_id);
let calling_user_id = session.user_id();
let calling_connection_id = session.connection_id;
let called_user_id = UserId::from_proto(request.called_user_id);
let initial_project_id = request.initial_project_id.map(ProjectId::from_proto);
if !session
.db()
.await
.has_contact(calling_user_id, called_user_id)
.await?
{
return Err(anyhow!("cannot call a user who isn't a contact"))?;
}
let incoming_call = {
let (room, incoming_call) = &mut *session
.db()
.await
.call(
room_id,
calling_user_id,
calling_connection_id,
called_user_id,
initial_project_id,
)
.await?;
room_updated(room, &session.peer);
mem::take(incoming_call)
};
update_user_contacts(called_user_id, &session).await?;View on GitHub (pinned to bc538def45)
Solutions
- Send a contact request (RequestContact) and wait for acceptance before calling; gate the call button on contact status
- If a request is already pending, wait for the recipient to accept rather than retrying the call
- Refresh contacts (the server pushes UpdateContacts) and only enable calls for accepted contacts
Example fix
// before
client.call_user(room_id, called_user_id, None).await?;
// after (check contact status first)
if !contacts.contains(&called_user_id) {
client.request_contact(called_user_id).await?;
self.ui.show_toast("Contact request sent; call once accepted");
return Ok(());
}
client.call_user(room_id, called_user_id, None).await?; Defensive patterns
Strategy: validation
Validate before calling
// Only call accepted contacts.
if !self.contacts.contains(&called_user_id) {
client.request_contact(called_user_id).await?;
self.ui.show_toast("Contact request sent; call once accepted");
return Ok(());
}
client.call_user(room_id, called_user_id, None).await?; Type guard
fn can_call(contacts: &HashSet<UserId>, user_id: UserId) -> bool {
contacts.contains(&user_id)
} Try / catch
if let Err(err) = client.call_user(room_id, called_user_id, None).await {
if err.to_string().contains("isn't a contact") {
client.request_contact(called_user_id).await?; // convert to a contact request
return Ok(());
}
return Err(err);
} Prevention
- Enable call actions only for accepted contacts in the UI
- Track pending contact requests separately so callers wait instead of retrying
- Keep the contact list synced from UpdateContacts pushes
When it happens
Trigger: CallUser sent to a user who never accepted your contact request (or whose request you never accepted); calling after the contact was removed on either side; calling a user id obtained from a room/channel rather than your contact list.
Common situations: UI exposing call buttons on channel members who are not contacts; contact request still pending; contact list out of sync after removing a contact.
Related errors
- banned users cannot invite
- failed to ring user
- cannot add yourself as a contact
- not a collaborator on this project
- not authorized to edit projects
AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16).
Data as JSON: /api/errors/8ea0358c0d549950.
Report an issue: GitHub.