Skip to content

Data channel connections fail with SDP m-line ordering error #1080

@parteeksingh24

Description

@parteeksingh24

Data channel mode (media: false) fails to establish a working connection. Both peers show "Connected" but the data channel doesn't open reliably, and sending messages fails with "Data channel may not be open."

Issue

Failed to execute 'setLocalDescription' on 'RTCPeerConnection': 
Failed to set local offer sdp: The order of m-lines in subsequent 
offer doesn't match order from previous offer/answer.

Reproduced in the SDK Explorer WebRTC demo (Data Channel tab) by joining the same room from two browser tabs. The error appears on one or both peers. Screen sharing in the Video Call tab works fine.

Environment: Chrome (latest), @agentuity/frontend current main, @agentuity/react useWebRTCCall hook with media: false + dataChannels: [{ label: 'chat', ordered: true }].

Additional Reproduction: Media + Data Channel

The same SDP error also triggers when combining media streams (audio/video) with a data channel in the same peer connection. Adding dataChannels: [{ label: 'media-state', ordered: true }] to a video call config (with media: true) reliably reproduces the m-line ordering error, since the data channel setup triggers renegotiation that collides with the media negotiation.

This confirms the issue is not specific to data-channel-only mode. It's a general lack of collision detection in onnegotiationneeded (~line 846) versus the guarded path in handleRemoteSDP (~line 924).

Potential Cause

Appears to be a race condition in the perfect negotiation implementation in WebRTCManager. Two code paths create offers independently:

  1. Explicit path (createPeerSession, ~line 898): calls createOffer() then setLocalDescription(offer) when the offerer joins
  2. Implicit path (onnegotiationneeded handler, ~line 846): calls setLocalDescription() without createOffer(), which auto-generates a new offer

When data channels are created, onnegotiationneeded fires and hits the implicit path while the explicit offer is still in flight. The auto-generated offer may have different m-line ordering than the first, which Chrome rejects per the WebRTC spec.

The onnegotiationneeded handler also appears to be missing the offer collision guard that handleRemoteSDP() already has (lines 923-927). So the offerer can generate a second offer while signalingState !== 'stable'.

Data-channel-only mode seems to hit this more reliably than media-enabled mode, likely because there are no audio/video tracks to stabilize the m-line structure.

Potential Fixes

  1. Add collision detection to the onnegotiationneeded handler: check session.makingOffer || pc.signalingState !== 'stable' before proceeding, mirroring the logic already in handleRemoteSDP()?
  2. Use explicit createOffer() in the onnegotiationneeded handler instead of bare setLocalDescription()?
  3. Make the answer path explicit too: createAnswer() + setLocalDescription(answer) in handleRemoteSDP() instead of bare setLocalDescription()?

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions