zed-industries/zed · warning · Error::Internal
could not find call to decline
Error message
could not find call to decline
What it means
Thrown by decline_call when expected_room_id is Some but no pending room_participant row matches the filter (called user, unanswered, null answering_connection, matching room). The Some(room_id) argument is an assertion that a specific ring exists; when the row is gone the server refuses instead of silently succeeding. With expected_room_id None the same lookup returns Ok(None) instead of erroring.
Source
Thrown at crates/collab/src/db/queries/rooms.rs:242
expected_room_id: Option<RoomId>,
user_id: UserId,
) -> Result<Option<TransactionGuard<proto::Room>>> {
self.optional_room_transaction(|tx| async move {
let mut filter = Condition::all()
.add(room_participant::Column::UserId.eq(user_id))
.add(room_participant::Column::AnsweringConnectionId.is_null());
if let Some(room_id) = expected_room_id {
filter = filter.add(room_participant::Column::RoomId.eq(room_id));
}
let participant = room_participant::Entity::find()
.filter(filter)
.one(&*tx)
.await?;
let participant = if let Some(participant) = participant {
participant
} else if expected_room_id.is_some() {
return Err(anyhow!("could not find call to decline"))?;
} else {
return Ok(None);
};
let room_id = participant.room_id;
room_participant::Entity::delete(participant.into_active_model())
.exec(&*tx)
.await?;
let room = self.get_room(room_id, &tx).await?;
Ok(Some((room_id, room)))
})
.await
}
pub async fn cancel_call(
&self,
room_id: RoomId,View on GitHub (pinned to bc538def45)
Solutions
- Pass expected_room_id: None when you want a graceful no-op if the ring already vanished; keep Some(room_id) only when your UI is certain the ring is live
- Drop the incoming-call notification from the UI as soon as a cancel/room-updated event arrives so decline cannot be sent for a dead ring
- Treat this error as 'call already gone' and simply close the notification, not as a failure
Example fix
// before
client.decline_call(Some(room_id)).await?;
// after (tolerate vanished rings)
match client.decline_call(Some(room_id)).await {
Ok(_) => {}
Err(err) if err.to_string().contains("could not find call to decline") => {
// ring was cancelled or expired; nothing to do
}
Err(err) => return Err(err),
} Defensive patterns
Strategy: try-catch
Validate before calling
// Only decline rings the client still believes are live.
if let Some(ring) = self.incoming_calls.get(&room_id) {
client.decline_call(Some(room_id)).await?;
self.incoming_calls.remove(&room_id);
} else {
log::debug!("no live ring for room {room_id}; skipping decline");
} Try / catch
match client.decline_call(Some(room_id)).await {
Ok(_) => {}
Err(err) if err.to_string().contains("could not find call to decline") => {
self.incoming_calls.remove(&room_id); // ring vanished; benign
}
Err(err) => return Err(err),
} Prevention
- Pass Some(room_id) only for rings currently shown as active
- Remove incoming-call notifications as soon as cancel/room-updated events arrive
- Idempotently handle double-decline in the UI (disable the button after first tap)
When it happens
Trigger: Declining a ring that the caller already cancelled (CancelCall deleted the row); declining after the ring timed out and was cleaned up; passing the wrong room_id from a stale incoming-call notification; double-clicking decline so the second request finds nothing.
Common situations: Race between the caller cancelling and the callee declining; UI showing a stale ring notification after the room state changed; automated tests declining fabricated calls.
Related errors
- room does not exist or was already joined
- banned users cannot invite
- could not update room participant location
- could not update room participant role
- no such project
AI-assisted analysis of zed-industries/zed@bc538def45 (2026-08-16).
Data as JSON: /api/errors/ff97c0d6598b9c50.
Report an issue: GitHub.