hyperledger/fabric · info

channel is closed

Error message

channel is closed

What it means

inProcStream.Recv returns this error when the internal recv channel has been closed and drained. In Fabric's in-process chaincode launcher, the receive channel is closed when the stream/shim is shutting down, so any further Recv() call gets ok==false and this sentinel error is produced. It signals that the chaincode stream is no longer usable and the receiver should stop reading.

Source

Thrown at core/scc/inprocstream.go:51

}

func (s *inProcStream) Send(msg *pb.ChaincodeMessage) (err error) {
	// send may happen on a closed channel when the system is
	// shutting down. Just catch the exception and return error
	defer func() {
		if r := recover(); r != nil {
			err = SendPanicFailure(fmt.Sprintf("%s", r))
			return
		}
	}()
	s.send <- msg
	return
}

func (s *inProcStream) Recv() (*pb.ChaincodeMessage, error) {
	msg, ok := <-s.recv
	if !ok {
		return nil, errors.New("channel is closed")
	}
	return msg, nil
}

func (s *inProcStream) CloseSend() error {
	s.closeOnce.Do(func() { close(s.send) })
	return nil
}

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Stop the Recv loop when this error is returned and treat it as normal stream termination
  2. Ensure the sender closes the channel only after all expected messages have been sent
  3. Guard with a done/ctx signal so Recv consumers exit before channel close
  4. Check for a shutdown/close being called prematurely on the inproc stream

Example fix

// before
for {
  msg, err := stream.Recv()
  if err != nil { log.Fatal(err) }
  handle(msg)
}
// after
for {
  msg, err := stream.Recv()
  if err != nil {
    if err.Error() == "channel is closed" { return } // normal shutdown
    log.Fatal(err)
  }
  handle(msg)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Go: check liveness before consuming
select {
case msg, ok := <-stream.RecvChan(): // if such access is available
    if !ok { return } // closed
default:
}

Try / catch

// Go
msg, err := stream.Recv()
if err != nil {
    if err.Error() == "channel is closed" {
        return nil // expected termination, stop loop gracefully
    }
    return fmt.Errorf("recv failed: %w", err)
}

Prevention

When it happens

Trigger: Calling Recv() on an *inProcStream after CloseSend/shutdown has closed the s.recv channel — e.g. the chaincode handler finished and closed the channel while a goroutine (or the test helpers TestSend/TestRecvChannelClosedError) is still calling Recv().

Common situations: In-process chaincode lifecycle teardown racing with a still-active Recv loop; unit tests exercising the stream close path; peer shutdown while chaincode stream handlers are pending.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/45d7f0b228d2ceae. Report an issue: GitHub.