Skip to content

Hang up the original leg after a successful blind transfer - #18

Merged
calebtt merged 2 commits into
masterfrom
fix/issue-14-transfer-hangup
Sep 26, 2026
Merged

calebtt merged 2 commits into
masterfrom
fix/issue-14-transfer-hangup

Conversation

@calebtt

@calebtt calebtt commented Sep 26, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #14.

SipClient.BlindTransferAsync is documented as "The original call leg is hung up on success", but it never hung up. Against a VitalPBX (Asterisk 20.14) lab PBX, the PBX completed the transfer and took the transferor's channel out of the bridge without hanging it up. The client stayed in the call:

  • IsCallActive stayed true, and CallEnded was not raised.
  • sipbot serve emitted no ended after transfer.
  • The next dial failed with already handling a call.

This was seen on v0.1.2 and master.

When the REFER is accepted, BlindTransferAsync now calls Hangup() after raising TransferSucceeded. That sends BYE and raises CallEnded through the usual path, so sipbot serve emits ended. A declined, failed, or cancelled transfer leaves the call up, as before. AGENTS.md now describes this.

Tests

New BlindTransferTests: a SipClient calls a plain SIPUserAgent over loopback, with no PBX needed.

  • Accepted REFER (202): the caller's CallEnded fires, IsCallActive becomes false, and the transferee receives a BYE. This test fails on current master.
  • Declined REFER: BlindTransferAsync returns false and the call stays up.

dotnet test: 62 passed (60 existing + 2 new). The new tests passed 5 of 5 repeated runs. Build warnings unchanged (17).

Compatibility

SipBotOpen and homeline call BlindTransferAsync and never hang up afterwards, so their leg currently stays up after a transfer. With this change it ends, raising CallEnded, which matches the documented behavior. JSONL event and field names are unchanged. transfer is now followed by ended.

🤖 Generated with Claude Code

calebtt and others added 2 commits September 25, 2026 19:46
BlindTransferAsync is documented to hang up the original call leg on
success, but it never did. Against Asterisk/VitalPBX the PBX completes the
transfer and takes the transferor's channel out of the bridge without
hanging it up, so SipClient stayed in the call: IsCallActive stayed true,
CallEnded was not raised, and sipbot serve emitted no `ended` and refused
the next dial with "already handling a call".

On success (REFER accepted) SipClient now calls Hangup(), which sends BYE
and raises CallEnded. A declined or failed transfer leaves the call up.

Closes #14

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After accepting the REFER, the test's SIPUserAgent starts its own call to
the transfer target, which can replace its dialog before the caller's BYE
arrives; on the CI runner OnCallHungup then never fired. Watch for the BYE
on the callee's transport instead, which does not depend on the transferee's
dialog handling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@calebtt

calebtt commented Sep 26, 2026

Copy link
Copy Markdown
Owner Author

The first CI run failed in Accepted_transfer_hangs_up_the_original_leg on its last assertion ("the transferee never received a BYE"). The caller-side checks had passed: CallEnded fired and IsCallActive was false.

The cause was a race in the test's callee. After accepting the REFER, SIPSorcery's SIPUserAgent starts its own call to the transfer target, and on the runner that replaced its dialog before the caller's BYE arrived, so OnCallHungup never fired. Commit 30fb331 now checks for the BYE on the callee's transport instead. It passed 8 of 8 local runs, and CI is green.

🤖 Generated with Claude Code

@calebtt
calebtt merged commit e7bedee into master Sep 26, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BlindTransferAsync does not hang up the original call leg after a successful transfer

1 participant