Why does dialProtocol (JS libp2p) lead to "Too many inbound protocol streams"?

Hi, I’m trying to write a small file transfer app using libp2p. I’ve been following the examples and so far I’ve had good success with getting my nodes to discover each other, spin up the DHT, etc.

I’m having trouble understanding the right way to use dialProtocolto exchange messages. I’ve created two handlers (let’s call them give and take). The idea is that a peer will query the DHT for a CID, and then dialProtocol(provider, '/give'). This gives me a stream on which I send the CID that I’d like to get.

The peer now does some work and sends a block of data back to the remote peer by calling dialProtocol(remotePeer, '/take') and writing the necessary blocks to the resulting stream. The problem is that this ultimately gets me to this error:

libp2p:mplex:stream:trace receiver stream 34 abort Error: Too many inbound protocol streams for protocol "/give" - limit 32
    at <snip>/node_modules/libp2p/dist/src/upgrader.js:273:55
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
  code: 'ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS'
}

I understand what the error means, but I’m confused about why it’s happening. From what I understand, the stream that I get after each dialProtocol is closed after the write is done. This is also evident from the debug logs below:

  libp2p:mplex:trace incoming message { id: 33, type: 'CLOSE_INITIATOR (4)' } +3ms
  libp2p:mplex:stream:trace receiver stream 33 closeRead +126ms
  libp2p:mplex:stream:trace receiver stream 33 source end - err: undefined +0ms
  libp2p:mplex:stream:trace receiver stream 33 close +8ms
  libp2p:mplex:stream:trace receiver stream 33 closeRead +1ms
  libp2p:mplex:stream:trace receiver stream 33 closeWrite +0ms
  libp2p:mplex:trace receiver stream 33 send { id: 33, type: 'CLOSE_RECEIVER (3)' } +9ms
  libp2p:mplex:stream:trace receiver stream 33 sink end - err: undefined +0ms
  libp2p:mplex receiver stream with id 33 and protocol /take ended

If it isn’t possible to reuse the stream, and dialProtocol eventually hits a limit, what am I missing? How do I get messages going back and forth over my protocol?

I figured this out. It turns out I was only closing the stream at the sender’s end. It’s equally important to call stream.close inside the protocol handler at the receiver’s end. No more “too many inbound streams” after this.