iflytek/astron-agent · error · DOMException

InvalidAccessError

InvalidAccessError

Error message

Track already exists.

What it means

This DOMException comes from adapter.js's addStream shim: before delegating to the original RTCPeerConnection.addStream, it checks every track of the passed stream against the existing senders (getSenders().find(t => t.track === e)). If any track is already attached to this peer connection, it throws 'Track already exists.' (InvalidAccessError) instead of creating a duplicate sender.

Solutions

  1. Guard the call: only call addStream if no sender already carries the track (pc.getSenders().some(s => s.track === track)).
  2. Reuse the existing sender via sender.replaceTrack(newTrack) instead of adding the stream again.
  3. Create a fresh RTCPeerConnection (and close the old one) on reconnect instead of reusing a connection that already has the tracks.
  4. If the SDK internally re-adds the stream, reset the SDK/player session (stop/close then recreate) before retrying.

Example fix

// before
pc.addStream(localStream); // second call throws
// after
const alreadyAdded = localStream.getTracks().every(t =>
  pc.getSenders().some(s => s.track === t));
if (!alreadyAdded) pc.addStream(localStream);
Defensive patterns

Strategy: try-catch

Validate before calling

const isDuplicate = stream.getTracks().every(t => pc.getSenders().some(s => s.track === t));

Try / catch

try { pc.addStream(stream); }
catch (e) {
  if (e instanceof DOMException && e.name === 'InvalidAccessError') return;
  throw e;
}

Prevention

When it happens

Trigger: Calling pc.addStream(mediaStream) (or an SDK wrapper that does) with a stream whose tracks were previously added to the same RTCPeerConnection — e.g. calling addStream twice with the same MediaStream, or adding getUserMedia tracks individually then adding the whole stream.

Common situations: Re-subscribing or reconnecting avatar playback without tearing down the previous peer connection; React/Vue hot-reload or StrictMode double-invocation of an effect that calls addStream twice; retry logic that re-adds the local stream after a renegotiation failure.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of iflytek/astron-agent@5e758547a8 (2026-09-12). Data as JSON: /api/errors/888b5c5b6046400c. Report an issue: GitHub.

Appendix: source

Thrown at console/frontend/src/utils/avatar-sdk-web_3.1.2.1002/xrtc-player-BJTnVhG9.js:19323

  const t = e.RTCPeerConnection.prototype.addTrack;
  e.RTCPeerConnection.prototype.addTrack = function (e, i) {
    if (!i) return t.apply(this, arguments);
    this._shimmedLocalStreams = this._shimmedLocalStreams || {};
    const r = t.apply(this, arguments);
    return (
      this._shimmedLocalStreams[i.id]
        ? -1 === this._shimmedLocalStreams[i.id].indexOf(r) &&
          this._shimmedLocalStreams[i.id].push(r)
        : (this._shimmedLocalStreams[i.id] = [i, r]),
      r
    );
  };
  const i = e.RTCPeerConnection.prototype.addStream;
  e.RTCPeerConnection.prototype.addStream = function (e) {
    ((this._shimmedLocalStreams = this._shimmedLocalStreams || {}),
      e.getTracks().forEach(e => {
        if (this.getSenders().find(t => t.track === e))
          throw new DOMException('Track already exists.', 'InvalidAccessError');
      }));
    const t = this.getSenders();
    i.apply(this, arguments);
    const r = this.getSenders().filter(e => -1 === t.indexOf(e));
    this._shimmedLocalStreams[e.id] = [e].concat(r);
  };
  const r = e.RTCPeerConnection.prototype.removeStream;
  e.RTCPeerConnection.prototype.removeStream = function (e) {
    return (
      (this._shimmedLocalStreams = this._shimmedLocalStreams || {}),
      delete this._shimmedLocalStreams[e.id],
      r.apply(this, arguments)
    );
  };
  const n = e.RTCPeerConnection.prototype.removeTrack;
  e.RTCPeerConnection.prototype.removeTrack = function (e) {
    return (
      (this._shimmedLocalStreams = this._shimmedLocalStreams || {}),

View on GitHub (pinned to 5e758547a8)