fix(voice): re-check call state under lock before forcing agent_rejected

This commit is contained in:
Tanmay Deep Sharma
2026-05-20 16:41:48 +05:30
parent 2f36c3be9c
commit 4b2bbd38e7
@@ -28,9 +28,7 @@ class Api::V1::Accounts::ConferenceController < Api::V1::Accounts::BaseControlle
def destroy
call = resolve_call!
rejecting = agent_rejecting_before_pickup?(call)
# Tear down the provider conference before marking the call terminal: if
# end_conference raises, the call stays ringing (non-terminal) and remains
# repairable by later status webhooks instead of being stuck as failed.
# Tear down provider side first so a teardown failure leaves the call repairable.
Voice::Provider::Twilio::ConferenceService.new(call: call).end_conference
finalize_as_agent_reject!(call) if rejecting
render json: { status: 'success', id: call.conversation.display_id }
@@ -71,11 +69,14 @@ class Api::V1::Accounts::ConferenceController < Api::V1::Accounts::BaseControlle
end
def finalize_as_agent_reject!(call)
call.update!(
status: 'failed',
end_reason: 'agent_rejected',
accepted_by_agent_id: Current.user.id
)
Voice::CallMessageBuilder.new(call).update_status!(status: 'failed', agent: Current.user)
# Re-check under a row lock: a webhook may have accepted/completed the call
# while end_conference was in flight, so don't force agent_rejected on stale state.
rejected = call.with_lock do
next false unless agent_rejecting_before_pickup?(call)
call.update!(status: 'failed', end_reason: 'agent_rejected', accepted_by_agent_id: Current.user.id)
true
end
Voice::CallMessageBuilder.new(call).update_status!(status: 'failed', agent: Current.user) if rejected
end
end