Proper behaviour on session cleanup


  • Creating plainTransport
  • Creating consumer on it

In case of an error or when the producer closes (I have an observer on ‘close’) i would like to be a good memory citizen and clean up the objects created. This means:

  • I’m closing the consumer
  • I’m closing the plainTransport

All these functions succeed, but I’m getting a warning from mediasoup, which seemingly kept track of my doings and tries to close the consumer too (which is gone now (my traces are tagged with ‘webrtc’). The active cleanup from my side is done between cleanRecording enter and cleanRecording leave:

Note the WARN:

  webrtc:DEBUG:observer producer closed [] +5s
  webrtc:DEBUG:server producer closed [] +5s
  webrtc:INFO:server cleanRecording enter +5s
  mediasoup:Consumer close() +5s
  mediasoup:Channel request() [method:transport.closeConsumer, id:12] +1ms
  webrtc:DEBUG:observer consumer closed [] +1ms
  mediasoup:Transport close() +5s
  mediasoup:Channel request() [method:router.closeTransport, id:13] +0ms
  webrtc:DEBUG:observer transport closed [] +2ms
  webrtc:INFO:server cleanRecording leave +3ms
  mediasoup:Channel request succeeded [method:transport.closeProducer, id:11] +2ms
  mediasoup:WARN:Channel request failed [method:transport.closeConsumer, id:12]: Consumer not found [method:transport.closeConsumer] +0ms
  mediasoup:Channel request succeeded [method:router.closeTransport, id:13] +1ms

There is no such a warning, if I leave the consumer ‘as is’ and just close the planTransport:

  webrtc:DEBUG:observer producer closed [] +4s
  webrtc:DEBUG:server producer closed [] +4s
  webrtc:INFO:server cleanRecording enter +4s
  mediasoup:Transport close() +4s
  mediasoup:Channel request() [method:router.closeTransport, id:12] +1ms
  mediasoup:Consumer transportClosed() +4s
  webrtc:DEBUG:observer consumer closed [] +2ms
  webrtc:DEBUG:observer transport closed [] +0ms

  webrtc:INFO:server cleanRecording leave +1ms

  mediasoup:Channel request succeeded [method:transport.closeProducer, id:11] +1ms
  mediasoup:Channel request succeeded [method:router.closeTransport, id:12] +0ms

Question: What would be the correct behaviour now? Trust on medasoup and just close the plainTransport or do it all as described in (1) and ignoring the WARN?

Producer.close() internally closes and deletes all its consumers in the worker. The node process is notified of that, but it must have already sent the request to close the consumer to the worker at this moment.

You can just call transport.close(), it will take care of everything both in the node part and in the worker.


Ah, one additional question: OK, this is clear for the case the producer closes.

But what if I want to start my recording (which is the process behind)? In that case I don’t necessarily want to close to producer, but I want to close the plainTransport only. I suppose in this case I would have to close the consumer manually, right?

Producers are created by the transports. When a transport is closed, it closes all its producers that are not closed explicitly. You can hold a reference to the closed producer, but it will be a dead object.

1 Like

Not sure to understand.

What I’m doing for recording:

rtpTransport = await room.router.createPlainTransport({
            comedia: false,
            rtcpMux: false,
            listenIp: { ip: '', announcedIp: null },
            appData: { kind: "plain-recording", room: }
 await rtpTransport.connect({
            ip: '',
            port: rtpPort,
            rtcpPort: rtpPort + 1,      // RTCP
            rtcpMux: false

rtpConsumer = await rtpTransport.consume({
            rtpCapabilities: room.router.rtpCapabilities,
            paused: true,

So this is to be switched on and off. The producer is not touched, so I just consuming the producer’s RTP via a plainTransport.

Now I want to finish the recording. I suppose I will have to call rtpConsumer.close() and rtpTransport.close() in that case.

The transport closes producers and/or consumers that it created. In this case, rtpTransport.close() will implicitly close rtpConsumer, but the producer must be from another transport, so rtpTransport.close() has nothing to do with it; the producer will remain open. (However, producer.close() will implicitly close its consumers, no matter what transports they belong to.)

Yepp. Thanks. Makes sense

Additional info: Safest way is to check if the element is already closed before calling close.

If you take a look at mediasoup sources, you’ll find that every close() method does this in the first place. So if the check is just to decide whether to call close() or not, it is basically a waste of space.

I was just trying to get rid of the mediasoup warning, when I close the consumer manually. If I now check the state and do not call close, the warning does not appear.

Well, Consumer.close() and all other close() methods do exactly the same check internally and return immediately if the object state is ‘closed’. Thus, this check alone could not cause the warning to disappear.

The check in conjunction with omitting the call to consumer.close, in case it is closed, does.

Consumer methods look like this:

	get closed(): boolean
		return this.#closed;

	close(): void
		if (this.#closed)

if (!consumer.closed()) consumer.close();

has the same effect as


I’m currently not even able to reproduce this warning, so you might be right. I have tested with and without the check - same result.

But I have refactored a lot since my first post here, so at the moment I don’t see the warn anymore, that’s ok.

When a Producer is closed (or the Transport of a Consumer, or the Router of that Transport) the Consumer gets closed in the worker and then worker notifies Node layer, so there is a chance for the custom Node app to call consumer.close() while not yet closed in Node layer but already closed in worker, and hence the warning.

Ah, good. I was sure, that I have seen this warning. After making the test the warning disappeared, but I surely changed other things, especially while attempting to await things. That might have caused the little race condition I saw. I mistakenly assumed that the check and the following omission of the consumer.close was the reason.

Thanks to you both.