Repository navigation
Conversation
Dropping the request leaves the server transaction it opened standing, and that one transaction then blocks the dialog in both directions for the rest of the call: no re-INVITE of ours can be sent, so the session cannot be held or transferred, and every in-dialog INVITE arriving afterwards is answered 500 by the core without ever reaching the application. 491 is the response RFC 3261 s14.2 names for a request that arrives while an offer/answer exchange is outstanding, and it invites the sender to retry once we settle. Any final status would free the transaction; this one also keeps the exchange recoverable.
Session.rollbackOffer is called whenever an offer/answer exchange fails - on a non-2xx response to our own re-INVITE, and when we cannot answer one we received - so that the next exchange starts from a stable peer connection. It resolves without doing anything when the handler implements no rollbackDescription, and the web one did not, so an offer applied by a failed exchange stayed applied and every later attempt failed while creating its description, on a session that was otherwise still up. A stable connection has nothing to discard, so that resolves rather than raising; the pranswer states and a closed connection are refused the way the neighbouring methods refuse them.
23Skidoo
marked this pull request as ready for review
October 20, 2026 08:42
timwhite-nebula
approved these changes
Oct 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
https://app.clickup.com/t/2167239/869epm91r
An in-dialog INVITE that arrives while a session is still establishing — after the 200 OK, before we have sent our ACK — is dropped, and the server transaction it opened is left standing. That one transaction then blocks the dialog in both directions for the rest of the call: no re-INVITE of ours can be sent, so the session cannot be held or transferred, and every in-dialog INVITE arriving afterwards is answered
500by the core without ever reaching the application.The desktop app hits this on a call picked up with
*35, where the platform refreshes the connected identity 28ms after the 200 OK and 36ms before the app's ACK. A user sees a call that cannot be held, and pressing Transfer leaves the far end listening to the whole consultation.Two changes
Answer the request instead of dropping it.
491 Request Pendingis what RFC 3261 s14.2 names for a request arriving while an offer/answer exchange is outstanding, and it invites the sender to retry once we settle. Any final status would free the transaction; this one also keeps the exchange recoverable. Rejecting also surfaces the request todelegate.onInvite, so an application can still see the identity the refresh carried.Implement
rollbackDescriptionon the web session description handler.Session.rollbackOfferis already called whenever an offer/answer exchange fails — on a non-2xx to our own re-INVITE, and when we cannot answer one we received — so that the next exchange starts from a stable peer connection. It resolves without doing anything when the handler implements norollbackDescription, and the web one did not, so an offer applied by a failed exchange stayed applied and every later attempt failed while creating its description, on a session otherwise still up.Testing
Four specs added to the existing web handler suite, covering a stable connection, an offer we created, an offer we received, and that a description can be built again afterwards. Verified against a real
RTCPeerConnectionin headless Chrome: removing the rollback call makes that last one fail withInvalid signaling state have-local-offer, the error from the original report.The glare itself is covered outside this repo, driving this stack against a peer that sends its INVITE inside the window on purpose — red before these changes, green after, including on the app's own hold path.
Note on the suite. Three specs under
API Session … Bob accept(), 200 has no SDP - Invalid 200already fail onmain, before these changes and after them. Nothing here touches that path.They are caused by the Allow Invitation ACK without offer patch. Upstream treats a 2xx carrying no SDP, in answer to an offerless INVITE, as fatal — ACK, BYE, 481, terminate — and those specs assert exactly that. The patch's
inviteWithoutSdpbranch acknowledges and establishes instead, and the specs were never updated to match. Verified by bisection: pristine 0.21.2 is green,mainfails those three on every run, and removing only that branch makes the suite green again.Worth reconciling separately — either update the specs to the intended behaviour or gate the branch — because a permanently red baseline hides the next real regression.
Proposed upstream
This is an upstream bug, so the same two changes are proposed to onsip/SIP.js as onsip#1129. The fork ships it without waiting on that review; if upstream lands it first, this becomes a cherry-pick.