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
- Stop the Recv loop when this error is returned and treat it as normal stream termination
- Ensure the sender closes the channel only after all expected messages have been sent
- Guard with a done/ctx signal so Recv consumers exit before channel close
- 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
- Treat Recv errors as loop-termination conditions, not fatal crashes
- Close the recv channel only after all senders/receivers have finished
- Use context cancellation or a done channel to coordinate stream shutdown
- In tests, expect this error when exercising the closed-stream path
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
- Recv() error: %v, closing connection
- chaincode stream terminated
- request message is nil
- stream %d is stale
- channel members not initialized
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/45d7f0b228d2ceae.
Report an issue: GitHub.