{"record":{"id":"3aa85ba1bab5f860","repo":"zed-industries/zed","slug":"contact-already-requested","errorCode":null,"errorMessage":"contact already requested","messagePattern":"contact already requested","errorType":"validation","errorClass":"Error::Internal","httpStatus":null,"severity":"error","filePath":"crates/collab/src/db/queries/contacts.rs","lineNumber":172,"sourceCode":"                        (contact::Column::ShouldNotify, false.into()),\n                    ])\n                    .action_and_where(\n                        contact::Column::Accepted.eq(false).and(\n                            contact::Column::AToB\n                                .eq(a_to_b)\n                                .and(contact::Column::UserIdA.eq(id_b))\n                                .or(contact::Column::AToB\n                                    .ne(a_to_b)\n                                    .and(contact::Column::UserIdA.eq(id_a))),\n                        ),\n                    )\n                    .to_owned(),\n            )\n            .exec_without_returning(&*tx)\n            .await?;\n\n            if rows_affected == 0 {\n                Err(anyhow!(\"contact already requested\"))?;\n            }\n\n            Ok(self\n                .create_notification(\n                    receiver_id,\n                    rpc::Notification::ContactRequest {\n                        sender_id: sender_id.to_proto(),\n                    },\n                    true,\n                    &tx,\n                )\n                .await?\n                .into_iter()\n                .collect())\n        })\n        .await\n    }\n","sourceCodeStart":154,"sourceCodeEnd":190,"githubUrl":"https://github.com/zed-industries/zed/blob/bc538def4545534201bbfcac4e95ac34ea6501b6/crates/collab/src/db/queries/contacts.rs#L154-L190","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nclient.send_contact_request(my_id, their_id).await?; // can fire twice\n\n// after\nif !outgoing_requests.contains(&their_id) && !contacts.contains(&their_id) {\n    client.send_contact_request(my_id, their_id).await?;\n    outgoing_requests.insert(their_id);\n}","handlingStrategy":"validation","validationCode":"// Check relationship state before requesting\nif db.has_contact(sender_id, receiver_id).await? {\n    return Ok(()); // already contacts\n}\nif outgoing_pending_requests.contains(&receiver_id) {\n    return Ok(()); // already requested\n}\ndb.send_contact_request(sender_id, receiver_id).await?;","typeGuard":null,"tryCatchPattern":"match db.send_contact_request(sender_id, receiver_id).await {\n    Ok(batch) => Ok(batch),\n    Err(err) if err.to_string().contains(\"contact already requested\") => {\n        Err(anyhow!(\"contact request already pending or already contacts\"))\n    }\n    Err(err) => Err(err),\n}","preventionTips":["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"],"tags":["collab","contacts","duplicate","database"],"backgroundTag":null,"analyzedSha":"bc538def4545534201bbfcac4e95ac34ea6501b6","analyzedAt":"2026-08-16T07:30:46.435Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}