Skip to content

fix(transport-webrtc): abort stream when datachannel send throws#3469

Open
tabcat wants to merge 5 commits intolibp2p:mainfrom
tabcat:fix/webrtc-send
Open

fix(transport-webrtc): abort stream when datachannel send throws#3469
tabcat wants to merge 5 commits intolibp2p:mainfrom
tabcat:fix/webrtc-send

Conversation

@tabcat
Copy link
Copy Markdown
Member

@tabcat tabcat commented Apr 19, 2026

Description

The node-datachannel polyfill caches RTCDataChannel.readyState and updates it via setImmediate when the native channel closes. This creates a window where the cached readyState still reports 'open' while the underlying native channel is already closed. In that window, channel.send() passes the readyState !== 'open' guard in WebRTCStream._sendMessage and calls into the native binding, which throws "DataChannel is closed" synchronously.

The throw was uncaught because _sendMessage did not wrap the channel.send calls, surfacing as an unhandled error in CI (observed in https://github.com/libp2p/js-libp2p/actions/runs/24605305629/job/71950609115?pr=3459).

This PR:

  • Adds a test that reproduces the divergence by creating a real datachannel pair, closing the peer connection synchronously, and asserting that send() aborts the stream cleanly.
  • Wraps channel.send in a try/catch and calls this.abort(err) on synchronous throw, so the error surfaces as a transport failure on the stream rather than an uncaught exception.

Related #3459.

Notes & open questions

The first commit (test only) is expected to fail CI to demonstrate the error path. The fix commit will be pushed next.

Change checklist

  • I have performed a self-review of my own code
  • I have made corresponding changes to the documentation if necessary (this includes comments as well)
  • I have added tests that prove my fix is effective or that my feature works

…gence

Triggers the JS-vs-native state divergence in node-datachannel polyfill by
closing the peer connection synchronously before send(), so the cached
readyState is still 'open' while the native channel is closed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@tabcat tabcat requested a review from a team as a code owner April 19, 2026 11:49
tabcat and others added 4 commits April 19, 2026 18:53
channel.send can throw synchronously when the native libdatachannel
state diverges from the polyfill's cached readyState. Wrap the call
and abort the stream so the error surfaces as a transport failure
instead of going uncaught.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The JS-vs-native state divergence is specific to the node-datachannel
polyfill; native browser WebRTC does not reproduce it. Also add
libdatachannel to the spell-check dictionary for the new fix comment.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@tabcat tabcat requested a review from dozyio April 19, 2026 16:07
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.

1 participant