{"record":{"id":"8ea0358c0d549950","repo":"zed-industries/zed","slug":"cannot-call-a-user-who-isn-t-a-contact","errorCode":null,"errorMessage":"cannot call a user who isn't a contact","messagePattern":"cannot call a user who isn't a contact","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/collab/src/rpc.rs","lineNumber":1754,"sourceCode":"\n/// Call someone else into the current room\nasync fn call(\n    request: proto::Call,\n    response: Response<proto::Call>,\n    session: MessageContext,\n) -> Result<()> {\n    let room_id = RoomId::from_proto(request.room_id);\n    let calling_user_id = session.user_id();\n    let calling_connection_id = session.connection_id;\n    let called_user_id = UserId::from_proto(request.called_user_id);\n    let initial_project_id = request.initial_project_id.map(ProjectId::from_proto);\n    if !session\n        .db()\n        .await\n        .has_contact(calling_user_id, called_user_id)\n        .await?\n    {\n        return Err(anyhow!(\"cannot call a user who isn't a contact\"))?;\n    }\n\n    let incoming_call = {\n        let (room, incoming_call) = &mut *session\n            .db()\n            .await\n            .call(\n                room_id,\n                calling_user_id,\n                calling_connection_id,\n                called_user_id,\n                initial_project_id,\n            )\n            .await?;\n        room_updated(room, &session.peer);\n        mem::take(incoming_call)\n    };\n    update_user_contacts(called_user_id, &session).await?;","sourceCodeStart":1736,"sourceCodeEnd":1772,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/collab/src/rpc.rs#L1736-L1772","documentation":"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.","triggerScenarios":"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.","commonSituations":"UI exposing call buttons on channel members who are not contacts; contact request still pending; contact list out of sync after removing a contact.","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"],"exampleFix":"// before\nclient.call_user(room_id, called_user_id, None).await?;\n\n// after (check contact status first)\nif !contacts.contains(&called_user_id) {\n    client.request_contact(called_user_id).await?;\n    self.ui.show_toast(\"Contact request sent; call once accepted\");\n    return Ok(());\n}\nclient.call_user(room_id, called_user_id, None).await?;","handlingStrategy":"validation","validationCode":"// Only call accepted contacts.\nif !self.contacts.contains(&called_user_id) {\n    client.request_contact(called_user_id).await?;\n    self.ui.show_toast(\"Contact request sent; call once accepted\");\n    return Ok(());\n}\nclient.call_user(room_id, called_user_id, None).await?;","typeGuard":"fn can_call(contacts: &HashSet<UserId>, user_id: UserId) -> bool {\n    contacts.contains(&user_id)\n}","tryCatchPattern":"if let Err(err) = client.call_user(room_id, called_user_id, None).await {\n    if err.to_string().contains(\"isn't a contact\") {\n        client.request_contact(called_user_id).await?; // convert to a contact request\n        return Ok(());\n    }\n    return Err(err);\n}","preventionTips":["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"],"tags":["contacts","voice-calls","authorization","rpc","server"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}