Skip to content

fix(transport-webrtc): handle terminal peer connection states and sig…#3406

Open
paschal533 wants to merge 5 commits intolibp2p:mainfrom
paschal533:fix/webrtc-terminal-peer-connection-states
Open

fix(transport-webrtc): handle terminal peer connection states and sig…#3406
paschal533 wants to merge 5 commits intolibp2p:mainfrom
paschal533:fix/webrtc-terminal-peer-connection-states

Conversation

@paschal533
Copy link
Contributor

@paschal533 paschal533 commented Mar 13, 2026

Fix: WebRTC terminal peer connection states, signaling race condition, and stream muxer test reliability

Problems fixed

  1. Half-open connection when ICE fails during SDP exchange

When a WebRTC private-to-private connection fails to establish (WiFi disabled, NAT traversal fails, ICE times out),
the dialing side could remain in a "connected" state while the listening side correctly detected the failure. This
left a leaked, half-open connection the connection manager never cleaned up.

The root cause: RTCPeerConnectionMultiaddrConnection only checked connectionState changes via onconnectionstatechange which does not fire for state transitions that already occurred before the handler was
registered. Added an explicit check of the initial connectionState at construction time.

Closes #2646

  1. UnexpectedEOFError in readCandidatesUntilConnected

A race condition caused the WebRTC "simple" transport compliance test to fail intermittently: the listener closes its
signaling stream just before the initiator's connectionstatechange: connected event fires. The initiator then
receives an EOF on the signaling stream and throws an UnexpectedEOFError, discarding a perfectly good peer
connection.

Fixed by moving the connectedPromise outside the try block in readCandidatesUntilConnected. If the signaling stream
throws EOF and the peer connection isn't yet 'connected', we now await the connectedPromise, if the PC connects
anyway (just after the EOF), the error is swallowed; if it fails, the error is re-thrown.

  1. 'disconnected' incorrectly treated as a terminal peer connection state

The onconnectionstatechange handler treated 'disconnected' the same as 'failed' and 'closed', calling
onTransportClosed() and peerConnection.close() immediately. WebRTC's 'disconnected' state is transient and the ICE
agent may recover to 'connected' without intervention

In Firefox, this caused a spurious DataChannel close during large data transfers, which raced against
sendCloseWrite's pEvent(channel, 'close') listener registration. If the channel closed before the listener was
registered, the FIN_ACK was missed, the finAckTimeout fired, and all branches of Promise.any rejected with an
AggregateError.

Fixed by only treating 'failed' and 'closed' as terminal states.

  1. Flaky stream muxer compliance test (calling abort aborts streams)

The test raced a 70ms timeout against pipes using await delay(10) per iteration which is leaving only a 20ms window after dialer.abort() at t=50ms for the abort to propagate. On loaded CI runners, NodeJS timer imprecision routinely exceeds
this margin.

Increased the timeout to 2000ms. On success, the test exits well under 100ms; the 2000ms only applies when abort
genuinely fails to propagate.

Test plan

  • Existing WebRTC transport tests pass (Chrome, Firefox)
  • @libp2p/integration-tests WebRTC "simple" compliance test passes
  • @libp2p/mplex calling abort aborts streams passes reliably
  • No regression on happy-path connected peers

@paschal533 paschal533 marked this pull request as ready for review March 13, 2026 14:38
@paschal533 paschal533 requested a review from a team as a code owner March 13, 2026 14:38
@Faolain
Copy link

Faolain commented Mar 20, 2026

minor nit but looks like pre-existing TODO: were stripped?

@paschal533
Copy link
Contributor Author

minor nit but looks like pre-existing TODO: were stripped?

Thanks for catching that... those slipped in during reformatting. I fogot to restored them in the latest commit. I will do that right away. Thank you sm

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.

When failing to establish WebRTC connection a "half open" connection can occur

2 participants