# Cannot assign to read only property 'role' when calling transport.consume

**URL:** <https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951>\
**Category:** mediasoup libraries\
**Created:** [March 29, 2024, 4:44am UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951 "2024-03-29T04:44:43Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Fedaz](https://avatars.discourse-cdn.com/v4/letter/f/4491bb/32.png) [@Fedaz](https://mediasoup.discourse.group/u/Fedaz)\
**Post date:** [March 29, 2024, 4:44am UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951/1 "2024-03-29T04:44:43Z")

</div>

Calling transport.consume is resulting in a readonly error for me:

```auto
                  const consumer = transport
                    .consume({
                      id,
                      producerId,
                      kind,
                      rtpParameters,
                    })
                    .then((res) => {
                      console.log("!!res", res)
                    })
                    .catch((e) => {
                      console.log("!!e", e)
                    })

```

The params that I’m passing in come from:

```auto
const { transport, params } = await createWebRtcTransport(router)

```

the error:

```auto
TypeError: Cannot assign to read only property 'role' of object '#<Object>'
    at RemoteSdp.updateDtlsRole (RemoteSdp.js:88:35)
    at Chrome111.setupTransport (Chrome111.js:672:25)
    at Chrome111.receive (Chrome111.js:528:24)
    at async transport.createPendingConsumers() [as task] (webpack://web-matchapp/node_modules/mediasoup-client/lib/Transport.js?:564:33)
    at async AwaitQueue.execute (index.js:172:28)

```

I can try to provide more info if anyone could suggest which information might be useful? or I could post my entire back and forth between client and server if that level of information would be required to troubleshoot this. Sorry if my question is vague, I’m just in the process of learning to use the library.

---

<div class="post-metadata">

**Author:** ![ibc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/ibc/32/1540_2.png) [@ibc](https://mediasoup.discourse.group/u/ibc)\
**Post date:** [March 29, 2024, 9:18am UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951/2 "2024-03-29T09:18:32Z")

</div>

> [@Fedaz](#):
>
> ```auto
> const consumer = transport
> .consume({
> id,
> producerId,
> kind,
> rtpParameters,
> })
> 
> ```

Please read the API documentation properly. The above code doesn’t make sense. The returned value of transport.consume() is not a Consumer but a Promise that resolves to a Consumer.

---

<div class="post-metadata">

**Author:** ![Fedaz](https://avatars.discourse-cdn.com/v4/letter/f/4491bb/32.png) [@Fedaz](https://mediasoup.discourse.group/u/Fedaz)\
**Post date:** [March 29, 2024, 3:09pm UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951/3 "2024-03-29T15:09:06Z")

</div>

Sorry, do you mean that the variable name I’ve chosen doesn’t make sense because it won’t actually be a consumer that’s returned but rather a promise of a consumer. The error I’m receiving is occurring within the `transport.consume` call, rather than with anything I’m doing with my perhaps incorrectly named variable. Not sure if I’m just misunderstanding what you’re saying though.

---

<div class="post-metadata">

**Author:** ![ibc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/ibc/32/1540_2.png) [@ibc](https://mediasoup.discourse.group/u/ibc)\
**Post date:** [March 29, 2024, 3:17pm UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951/4 "2024-03-29T15:17:47Z")

</div>

Yes, that’s a separate naming problem. In your case you are probably using Vue or MobX or something like that and passing their special proxy objects to the mediasoup-client API instead of passing pure JS objects.

---

<div class="post-metadata">

**Author:** ![Fedaz](https://avatars.discourse-cdn.com/v4/letter/f/4491bb/32.png) [@Fedaz](https://mediasoup.discourse.group/u/Fedaz)\
**Post date:** [March 30, 2024, 1:11am UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951/5 "2024-03-30T01:11:41Z")

</div>

Ah okay, will try to improve my naming conventions, thanks. I don’t think I’m using any kind of proxy objects or anything like that. I really just have this one monolithic frontend function and two backend functions and it works almost all of the way through (i.e. I don’t get any issue when I check `canConsume`, only after, when I attempt to consume via the client consumerTransport):

Client function:

```js
  const handleViewStream = () => {
    viewStream({ variables: { params: { username: streamer.username } } }).then(
      async ({ data }) => {
        if (data && "viewStream" in data) {
          const {
            viewStream: { params, routerCapabilities },
          } = data

          try {
            const consumeDevice = new mediasoup.Device()

            await consumeDevice.load({
              routerRtpCapabilities: routerCapabilities,
            })

            const consumerTransport = consumeDevice.createRecvTransport(params)

            consumerTransport.on(
              "connect",
              async ({ dtlsParameters }, callback, errback) => {
                console.log("!!!!!!!!!!!!!!!!!!!connect")

                // socket?.emit("connectConsumerTransport", {
                // transportId: transport.id,
                // dtlsParameters,
                // streamerUsername: streamer.username,
                // })
                // socket.on("consumerConnected", async function (data) {
                // console.log("!![ms]consumerConnected", data)
                // callback()
                // })
              }
            )

            consumerTransport.on("connectionstatechange", (state) => {
              console.log("!![ms]connectionStateChange", data)
              switch (state) {
                case "connecting":
                  console.log("!!Consumer: ICE State Connecting")
                  break
                case "connected":
                  console.log("!!Consumer: ICE State Connected")
                  // socket?.emit("resume", { streamerUsername: streamer.username })
                  break
                case "failed":
                  console.log("!!Consumer: ICE State Failed")
                  consumerTransport.close()
                  // socket?.emit("stopViewingStream", {
                  // streamerUsername: streamer.username,
                  // viewerUsername: user.username,
                  // })
                  break
                default:
                  break
              }
            })

            consume({
              variables: {
                rtpCapabilities: consumeDevice.rtpCapabilities,
                streamerUsername: streamer.username,
              },
            }).then(async ({ data }) => {
              if (data && "consume" in data) {
                const { subData } = data.consume
                const { id, producerId, kind, rtpParameters } = subData

                consumerTransport
                  .consume({
                    id,
                    producerId,
                    kind,
                    rtpParameters,
                  })
                  .then((res) => {
                    console.log("!!res", res)
                  })
                  .catch((e) => {
                    console.log("!!e", e)
                  })
              }
            })
          } catch (error) {
            if (error.name === "UnsupportedError") {
              console.log("!!Browser not supported")
            }
          }
        }
      }
    )
  }

```

Server side functions:

```auto
export const getStream = async (db: Kysely<Database>, { args, context }) => {
  try {
    const { user: currentUser } = context
    const {
      params: { username },
    } = args

    if (!currentUser) {
      return new Error("Not logged in")
    }

    if (!streams[username]) {
      return new Error("User is not live")
    }

    const routerCapabilities = await getRouterCapabilities()

    const { transport, params } = await createWebRtcTransport(router)

    streams[username] = {
      ...streams[username],
      consumerTransports: {
        ...streams[username].consumerTransports,
        [currentUser.username]: transport,
      },
    }

    return { params, routerCapabilities }
  } catch (error) {
    console.log("!!error", error)
    return new Error("Request could not be processed")
  }
}

export const consume = async (db: Kysely<Database>, { args, context }) => {
  try {
    const { user: currentUser } = context
    const { rtpCapabilities, streamerUsername } = args

    if (
      !router.canConsume({
        producerId: streams[streamerUsername].producer.id,
        rtpCapabilities,
      })
    ) {
      console.log("!!Cannot Consume")
      return
    }

    try {
      const newConsumer = await streams[streamerUsername].consumerTransports[
        currentUser.username
      ].consume({
        producerId: streams[streamerUsername].producer.id,
        rtpCapabilities,
        paused: streams[streamerUsername].producer.kind === "video",
      })

      streams[streamerUsername] = {
        ...streams[streamerUsername],
        consumers: {
          ...streams[streamerUsername].consumers,
          [currentUser.username]: newConsumer,
        },
      }

      const subData = {
        producerId: streams[streamerUsername].producer.id,
        id: newConsumer.id,
        kind: newConsumer.kind,
        rtpParameters: newConsumer.rtpParameters,
        type: newConsumer.type,
        producerPaused: newConsumer.producerPaused,
      }

      return {
        subData: subData,
        stream: streams[streamerUsername],
      }
    } catch (error) {
      console.log("!!error", error)
      return
    }
  } catch (error) {
    console.log("!!error", error)
  }
}

```

I guess my use of in-memory data structures is pretty horrible but I’m kind of aware of that. I’m just trying to learn at the moment so will improve things after. I think the producer stuff is all working now and I think almost all of the consumer stuff is working, it just seems to run into this issue of the read only property right at the end when I call `consumerTransport.consume`.

I appreciate you probably don’t have the time or desire to look through the above code. Just wanted to post it for the sake of a more complete thread and in case anyone else fancies taking a look.

---

<div class="post-metadata">

**Author:** ![ibc](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/ibc/32/1540_2.png) [@ibc](https://mediasoup.discourse.group/u/ibc)\
**Post date:** [March 30, 2024, 5:35pm UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951/6 "2024-03-30T17:35:33Z")

</div>

This is the mediasoup-client code involved into your issue:

```js
	private async setupTransport({
		localDtlsRole,
		localSdpObject,
	}: {
		localDtlsRole: DtlsRole;
		localSdpObject?: any;
	}): Promise<void> {
		if (!localSdpObject) {
			localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp);
		}

		// Get our local DTLS parameters.
		const dtlsParameters = sdpCommonUtils.extractDtlsParameters({
			sdpObject: localSdpObject,
		});

		// Set our DTLS role.
		dtlsParameters.role = localDtlsRole;

		// Update the remote DTLS role in the SDP.
		this._remoteSdp!.updateDtlsRole(
			localDtlsRole === 'client' ? 'server' : 'client'
		);

```

Here it parses a SDP (a string) here:

```js
localSdpObject = sdpTransform.parse(this._pc.localDescription.sdp);

```

Then it extracts DTLS parameters from the parsed SDP (which is just a JS object) calling:

```js
		const dtlsParameters = sdpCommonUtils.extractDtlsParameters({
			sdpObject: localSdpObject,
		});

```

which is as follows:

```js
export function extractDtlsParameters({
	sdpObject,
}: {
	sdpObject: any;
}): DtlsParameters {
	let setup = sdpObject.setup;
	let fingerprint = sdpObject.fingerprint;

	if (!setup || !fingerprint) {
		const mediaObject = (sdpObject.media || []).find(
			(m: { port: number }) => m.port !== 0
		);

		if (mediaObject) {
			setup ??= mediaObject.setup;
			fingerprint ??= mediaObject.fingerprint;
		}
	}

	if (!setup) {
		throw new Error('no a=setup found at SDP session or media level');
	} else if (!fingerprint) {
		throw new Error('no a=fingerprint found at SDP session or media level');
	}

	let role: DtlsRole | undefined;

	switch (setup) {
		case 'active': {
			role = 'client';

			break;
		}

		case 'passive': {
			role = 'server';

			break;
		}

		case 'actpass': {
			role = 'auto';

			break;
		}
	}

	const dtlsParameters: DtlsParameters = {
		role,
		fingerprints: [
			{
				algorithm: fingerprint.type,
				value: fingerprint.hash,
			},
		],
	};

	return dtlsParameters;
}

```

And then it changes the `role` value of the returned `dtlsParameters` object:

```js
dtlsParameters.role = localDtlsRole;

```

Nothing fancy here. So I hace no idea about why this happens to you but nothing in mediasoup-client internals should produce it.

---

<div class="post-metadata">

**Author:** ![snnz](https://yyz2.discourse-cdn.com/free1/user_avatar/mediasoup.discourse.group/snnz/32/693_2.png) [@snnz](https://mediasoup.discourse.group/u/snnz)\
**Post date:** [March 30, 2024, 7:48pm UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951/7 "2024-03-30T19:48:10Z")

</div>

`updateDtlsRole` modifies another dtlsParameters object - the one that is passed to `Device.createRecvTransport` (nested in its argument), then to the Transport constructor and the handler.  
Here it is hidden in the `data.viewStream.params`:

> [@Fedaz](#):
>
> ```auto
> const {
> viewStream: { params, routerCapabilities },
> } = data
> ...
> const consumerTransport = consumeDevice.createRecvTransport(params)
> 
> ```

There could be anything, depending on how this object was created.

---

<div class="post-metadata">

**Author:** ![Fedaz](https://avatars.discourse-cdn.com/v4/letter/f/4491bb/32.png) [@Fedaz](https://mediasoup.discourse.group/u/Fedaz)\
**Post date:** [March 31, 2024, 1:14am UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951/8 "2024-03-31T01:14:02Z")

</div>

params just looks like this:

```auto
    params: {
        id: string;
        iceParameters: IceParameters;
        iceCandidates: IceCandidate[];
        dtlsParameters: DtlsParameters;
        sctpParameters: SctpParameters;
    };

```

it comes from here:

```auto
const { transport, params } = await createWebRtcTransport(router)

return { params, routerCapabilities }

```

which is a call to:

```auto
import { Router } from "mediasoup/node/lib/types"
import { config } from "./config"

export const createWebRtcTransport = async (mediasoupRouter: Router) => {
  const { maxIncomeBitrate, initialAvailableOutgoingBitrate } =
    config.mediasoup.webRtcTransport

  const transport = await mediasoupRouter.createWebRtcTransport({
    listenIps: config.mediasoup.webRtcTransport.listenIps,
    enableUdp: true,
    enableTcp: true,
    preferUdp: true,
    initialAvailableOutgoingBitrate,
  })

  if (maxIncomeBitrate) {
    try {
      await transport.setMaxIncomingBitrate(maxIncomeBitrate)
    } catch (error) {
      console.log("!!error", error)
    }
  }

  return {
    transport,
    params: {
      id: transport.id,
      iceParameters: transport.iceParameters,
      iceCandidates: transport.iceCandidates,
      dtlsParameters: transport.dtlsParameters,
      sctpParameters: transport.sctpParameters,
    },
  }
}

```

---

<div class="post-metadata">

**Author:** ![Fedaz](https://avatars.discourse-cdn.com/v4/letter/f/4491bb/32.png) [@Fedaz](https://mediasoup.discourse.group/u/Fedaz)\
**Post date:** [March 31, 2024, 1:17am UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951/9 "2024-03-31T01:17:18Z")

</div>

I guess it sounds like ibc is correct, if this `params` object is supposed to be modified by `updateDtlsRole` as you say snnz and I’m getting a read-only error on it, then for some reason, it’s unable to modify the params object and that’s what I need to look into. Thanks for the help in troubleshooting this. Will feedback if I make progress in case it can help someone else.

---

<div class="post-metadata">

**Author:** ![Fedaz](https://avatars.discourse-cdn.com/v4/letter/f/4491bb/32.png) [@Fedaz](https://mediasoup.discourse.group/u/Fedaz)\
**Post date:** [March 31, 2024, 2:19am UTC](https://mediasoup.discourse.group/t/cannot-assign-to-read-only-property-role-when-calling-transport-consume/5951/10 "2024-03-31T02:19:03Z")

</div>

Yeah, you guys had it right. Tested with this and got a connect result:

```auto
let params = {
  ...data.viewStream.params,
  dtlsParameters: {
    role: data.viewStream.params.dtlsParameters.role,
    fingerprints: data.viewStream.params.dtlsParameters.fingerprints,
    },
  }

```

Thanks for the help, I appreciate it.
