title: WebRTC getting Failed to execute 'addIceCandidate' on RTCPeerConnection error on console but can still display remote and local videos
body: <p>I am trying to connect two peers using webRTC. I am able to display both local and remote videos correctly but as soon as the remote video appears, the candidate object becomes <code>null</code> and on the console it logs the following error message.</p>
  
  <pre><code>TypeError: Failed to execute 'addIceCandidate' on 'RTCPeerConnection': Candidate missing values for both sdpMid and sdpMLineIndex
  </code></pre>
  
  <p>I am using two separate laptops to test the connection and since both remote and local videos are showing, I think that I have connected two peers successfully but I'm not sure because of the error message.</p>
  
  <p>Any idea on why this might be happening? Am I even successfully connecting the two peers?</p>
  
  <p>Below is the code.</p>
  
  <p>Thank you!</p>
  
  <p><strong>Frontend</strong></p>
  
  <pre><code>import React, { Component } from 'react';
  import io from 'socket.io-client';
  class App extends Component {
    constructor(props) {
      super(props);
      this.room = 'test-room';
      this.socket = io.connect('http://localhost:5000');
      this.localPeerConnection = new RTCPeerConnection({
        iceServers: [
          {
            urls: 'stun:stun.l.google.com:19302'
          }
        ]
      });
      this.remotePeerConnection = new RTCPeerConnection({
        iceServers: [
          {
            urls: 'stun:stun.l.google.com:19302'
          }
        ]
      });
    };
  
    componentDidMount() {
      this.socket.on('connect', () =&gt; {
        this.socket.emit('join', this.room, err =&gt; {
          if (err) {
            console.error(err);
          } else {
            this.socket.on('offer', offer =&gt; {
              console.log('OFFER RECEIVED: ', offer);
              this.createAnswer(offer);
            });
  
            this.socket.on('candidate', candidate =&gt; {
              console.log('CANDIDATE RECEIVED', candidate);
              this.localPeerConnection.addIceCandidate(candidate).catch(error =&gt; console.error(error));
              this.remotePeerConnection.addIceCandidate(candidate).catch(error =&gt; console.error(error));
            });
  
            this.socket.on('answer', answer =&gt; {
              console.log('ANSWER RECEIVED:', answer);
              this.localPeerConnection.setRemoteDescription(answer);
            });
          }
        });
      });
    }
  
    startCall = async () =&gt; {
      this.localPeerConnection.onicecandidate = e =&gt; {
        const iceCandidate = e.candidate;
        this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
        console.log('candidate generated', e.candidate);
      };
  
      this.localPeerConnection.ontrack = e =&gt; {
        this.remoteVideo.srcObject = e.streams[0];
        console.log('REMOTE STREAM?: ', e.streams[0]);
      };
  
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false });
        for (const track of stream.getTracks()) {
          this.localPeerConnection.addTrack(track, stream);
        }
  
        this.localVideo.srcObject = stream;
        console.log('LOCAL STREAMS: ', this.localPeerConnection.getLocalStreams())
  
        return this.createOffer();
      } catch (error) {
        console.error(error);
      }
    }
  
    createOffer = async () =&gt; {
      try {
        const offer = await this.localPeerConnection.createOffer();
        await this.localPeerConnection.setLocalDescription(offer);
        await this.remotePeerConnection.setRemoteDescription(offer);
  
        this.socket.emit('offer', { room: this.room, offer });
        console.log('SENDING OFFER: ', offer);
      } catch (error) {
        console.error(error);
      }
    }
  
    createAnswer = async description =&gt; {
      this.remotePeerConnection.onicecandidate = e =&gt; {
        const iceCandidate = e.candidate;
        this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
        console.log('candidate generated', e.candidate);
      };
  
      this.remotePeerConnection.ontrack = e =&gt; {
        this.remoteVideo.srcObject = e.streams[0];
      };
  
      this.remotePeerConnection.setRemoteDescription(description)
        .then(() =&gt; navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false }))
        .then(stream =&gt; {
          for (const track of stream.getTracks()) {
            this.remotePeerConnection.addTrack(track, stream);
          }
  
          this.localVideo.srcObject = stream;
  
          return this.remotePeerConnection.createAnswer();
        })
        .then(answer =&gt; {
          this.remotePeerConnection.setLocalDescription(answer);
          return answer;
        })
        .then(answer =&gt; {
          this.socket.emit('answer', { room: this.room, answer });
          console.log('SENDING ANSWER: ', answer);
        })
        .catch(error =&gt; console.error(error))
    }
  
    render() {
      return (
        &lt;div&gt;
          &lt;h1&gt;Webrtc&lt;/h1&gt;
          &lt;div&gt;
            &lt;button onClick={this.startCall}&gt;CALL&lt;/button&gt;
          &lt;/div&gt;
          &lt;div style={{ display: 'flex' }}&gt;
            &lt;div&gt;
              &lt;video id='localVideo' autoPlay muted playsInline ref={ref =&gt; this.localVideo = ref} /&gt;
              &lt;p&gt;LOCAL VIDEO&lt;/p&gt;
            &lt;/div&gt;
            &lt;div&gt;
              &lt;video id='remoteVideo' autoPlay muted playsInline ref={ref =&gt; this.remoteVideo = ref} /&gt;
              &lt;p&gt;REMOTE VIDEO&lt;/p&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      );
    }
  }
  
  export default App;
  </code></pre>
  
  <p><strong>Server</strong></p>
  
  <pre><code>const express = require('express');
  
  const app = express();
  const server = require('http').createServer(app);
  const io = require('socket.io')(server);
  const PORT = process.env.PORT || 5000;
  
  const connections = [];
  const clients = [];
  
  io.set('origins', '*:*');
  io.on('connection', socket =&gt; {
    connections.push(socket);
    clients.push({ socket_id: socket.id });
    console.log('Connected: %s sockets connected ', connections.length);
  
    socket.on('join', (room, callback) =&gt; {
      const clients = io.sockets.adapter.rooms[room];
      const numClients = (typeof clients !== 'undefined') ? clients.length : 0;
      console.log('joined room', room);
      if (numClients &gt; 1) {
        return callback('already_full');
      }
      else if (numClients === 1) {
        socket.join(room);
        io.in(room).emit('ready');
      }
      else {
        socket.join(room);
      }
  
      callback();
    });
  
    socket.on('offer', (data) =&gt; {
      const { room, offer } = data;
      console.log('offer from: ', offer);
      socket.to(room).emit('offer', offer);
    });
  
    socket.on('answer', (data) =&gt; {
      const { room, answer } = data;
      console.log('answer from: ', answer);
      socket.to(room).emit('answer', answer);
    });
  
    socket.on('candidate', (data) =&gt; {
      const { room, candidate } = data;
      console.log('candidate: ', candidate);
      socket.to(room).emit('candidate', candidate);
    });
  
    socket.on('disconnect', () =&gt; {
      connections.splice(connections.indexOf(socket), 1);
      console.log('Disconnected: %s sockets connected, ', connections.length);
      clients.forEach((client, i) =&gt; {
        if (client.socket_id === socket.id) {
          clients.splice(i, 1);
        }
      });
    });
  });
  
  server.listen(PORT, () =&gt; {
    console.log(`Server listening on port ${PORT}`);
  });
  </code></pre>
  
  <p><strong>UPDATE</strong></p>
  
  <p>After reading jib's comment, I have modified my client js as follows.</p>
  
  <pre><code>import React, { Component } from 'react';
  import io from 'socket.io-client';
  
  class App extends Component {
    constructor(props) {
      super(props);
      this.room = 'test-room';
      this.socket = io.connect('http://localhost:5000');
      this.peerConnection = new RTCPeerConnection({
        iceServers: [
          {
            urls: 'stun:stun.l.google.com:19302'
          }
        ]
      });
    };
  
    componentDidMount() {
      this.socket.on('connect', () =&gt; {
        this.socket.emit('join', this.room, err =&gt; {
          if (err) {
            console.error(err);
          } else {
            this.socket.on('offer', offer =&gt; {
              console.log('OFFER RECEIVED: ', offer);
              this.createAnswer(offer);
            });
  
            this.socket.on('candidate', candidate =&gt; {
              this.peerConnection.addIceCandidate(candidate).catch(error =&gt; console.error(error));
              console.log('CANDIDATE RECEIVED', candidate);
            });
  
            this.socket.on('answer', answer =&gt; {
              console.log('ANSWER RECEIVED:', answer);
              this.peerConnection.setRemoteDescription(answer);
            });
          }
        });
      });
    }
  
    startCall = async () =&gt; {
      this.peerConnection.oniceconnectionstatechange = () =&gt; console.log('ICE CONNECTION STATE: ', this.peerConnection.iceConnectionState);
  
      this.peerConnection.onicecandidate = e =&gt; {
        const iceCandidate = e.candidate;
        this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
        console.log('candidate generated', e.candidate);
      };
  
      this.peerConnection.ontrack = e =&gt; {
        this.remoteVideo.srcObject = e.streams[0];
        console.log('REMOTE STREAMS: ', this.peerConnection.getRemoteStreams());
      };
  
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false });
        for (const track of stream.getTracks()) {
          this.peerConnection.addTrack(track, stream);
        }
  
        this.localVideo.srcObject = stream;
        console.log('LOCAL STREAMS: ', this.peerConnection.getLocalStreams())
  
        return this.createOffer();
      } catch (error) {
        console.error(error);
      }
    }
  
    createOffer = async () =&gt; {
      try {
        const offer = await this.peerConnection.createOffer();
        await this.peerConnection.setLocalDescription(offer);
        this.socket.emit('offer', { room: this.room, offer });
        console.log('SENDING OFFER: ', offer);
      } catch (error) {
        console.error(error);
      }
    }
  
    createAnswer = async description =&gt; {
      this.peerConnection.onicecandidate = e =&gt; {
        const iceCandidate = e.candidate;
        this.socket.emit('candidate', { room: this.room, candidate: iceCandidate });
        console.log('candidate generated', e.candidate);
      };
  
      this.peerConnection.ontrack = e =&gt; {
        this.remoteVideo.srcObject = e.streams[0];
      };
  
      this.peerConnection.setRemoteDescription(description)
        .then(() =&gt; navigator.mediaDevices.getUserMedia({ video: { width: 150, height: 150 }, audio: false }))
        .then(stream =&gt; {
          for (const track of stream.getTracks()) {
            this.peerConnection.addTrack(track, stream);
          }
  
          this.localVideo.srcObject = stream;
  
          return this.peerConnection.createAnswer();
        })
        .then(answer =&gt; {
          this.peerConnection.setLocalDescription(answer);
          return answer;
        })
        .then(answer =&gt; {
          this.socket.emit('answer', { room: this.room, answer });
          console.log('SENDING ANSWER: ', answer);
        })
        .catch(error =&gt; console.error(error))
    }
  
    render() {
      return (
        &lt;div&gt;
          &lt;h1&gt;Webrtc&lt;/h1&gt;
          &lt;div&gt;
            &lt;button onClick={this.startCall}&gt;CALL&lt;/button&gt;
          &lt;/div&gt;
          &lt;div style={{ display: 'flex' }}&gt;
            &lt;div&gt;
              &lt;video id='localVideo' autoPlay muted playsInline ref={ref =&gt; this.localVideo = ref} /&gt;
              &lt;p&gt;LOCAL VIDEO&lt;/p&gt;
            &lt;/div&gt;
            &lt;div&gt;
              &lt;video id='remoteVideo' autoPlay muted playsInline ref={ref =&gt; this.remoteVideo = ref} /&gt;
              &lt;p&gt;REMOTE VIDEO&lt;/p&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      );
    }
  }
  
  export default App;
  </code></pre>
  
  <p>The error on my console still persists... any idea why?</p>