zed-industries/zed · error · Error::Internal
contact already requested
Error message
contact already requested
What it means
Thrown by send_contact_request (crates/collab/src/db/queries/contacts.rs:172): the INSERT uses ON CONFLICT (user_id_a, user_id_b) that would auto-accept the other user's pending request, but only when the existing row is pending AND directed from the other user (action_and_where at contacts.rs:156-164). rows_affected == 0 means a row for the pair already exists in any other state: the sender already requested (same direction), or the pair are already accepted contacts.
Source
Thrown at crates/collab/src/db/queries/contacts.rs:172
(contact::Column::ShouldNotify, false.into()),
])
.action_and_where(
contact::Column::Accepted.eq(false).and(
contact::Column::AToB
.eq(a_to_b)
.and(contact::Column::UserIdA.eq(id_b))
.or(contact::Column::AToB
.ne(a_to_b)
.and(contact::Column::UserIdA.eq(id_a))),
),
)
.to_owned(),
)
.exec_without_returning(&*tx)
.await?;
if rows_affected == 0 {
Err(anyhow!("contact already requested"))?;
}
Ok(self
.create_notification(
receiver_id,
rpc::Notification::ContactRequest {
sender_id: sender_id.to_proto(),
},
true,
&tx,
)
.await?
.into_iter()
.collect())
})
.await
}
View on GitHub (pinned to bc538def45)
Solutions
- Track sent-request state client-side and hide/disable the request action once pending (the notification flow will surface acceptance)
- If the pair are already contacts, skip sending; has_contact (contacts.rs:107) tells you the accepted state
- On the server handler, consider mapping this error to a friendly 'request already pending' response instead of a generic 500
Example fix
// before
client.send_contact_request(my_id, their_id).await?; // can fire twice
// after
if !outgoing_requests.contains(&their_id) && !contacts.contains(&their_id) {
client.send_contact_request(my_id, their_id).await?;
outgoing_requests.insert(their_id);
} Defensive patterns
Strategy: validation
Validate before calling
// Check relationship state before requesting
if db.has_contact(sender_id, receiver_id).await? {
return Ok(()); // already contacts
}
if outgoing_pending_requests.contains(&receiver_id) {
return Ok(()); // already requested
}
db.send_contact_request(sender_id, receiver_id).await?; Try / catch
match db.send_contact_request(sender_id, receiver_id).await {
Ok(batch) => Ok(batch),
Err(err) if err.to_string().contains("contact already requested") => {
Err(anyhow!("contact request already pending or already contacts"))
}
Err(err) => Err(err),
} Prevention
- Disable the 'add contact' control as soon as a request is sent; re-enable only on decline/removal
- Never blind-retry send_contact_request — duplicates map to this error, not to success
- Rely on the ContactRequest notification flow to learn acceptance instead of re-requesting
When it happens
Trigger: User A sends a contact request to B twice (the pending row already points A→B, so the conflict-update doesn't match); A requests B when A and B are already accepted contacts; A re-sends after B already requested A but the row was since accepted.
Common situations: Client UI not disabling the 'add contact' button after a request is sent; retry logic duplicating the request RPC; both effects — user re-taps because the first response was slow.
Related errors
- not a collaborator on this project
- can't delete channel while a call is in progress
- no such contact request
- no thread found with ID: {id:?}
- not in a call
AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16).
Data as JSON: /api/errors/3aa85ba1bab5f860.
Report an issue: GitHub.