{"record":{"id":"45d7f0b228d2ceae","repo":"hyperledger/fabric","slug":"channel-is-closed","errorCode":null,"errorMessage":"channel is closed","messagePattern":"channel is closed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"info","filePath":"core/scc/inprocstream.go","lineNumber":51,"sourceCode":"}\n\nfunc (s *inProcStream) Send(msg *pb.ChaincodeMessage) (err error) {\n\t// send may happen on a closed channel when the system is\n\t// shutting down. Just catch the exception and return error\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\terr = SendPanicFailure(fmt.Sprintf(\"%s\", r))\n\t\t\treturn\n\t\t}\n\t}()\n\ts.send <- msg\n\treturn\n}\n\nfunc (s *inProcStream) Recv() (*pb.ChaincodeMessage, error) {\n\tmsg, ok := <-s.recv\n\tif !ok {\n\t\treturn nil, errors.New(\"channel is closed\")\n\t}\n\treturn msg, nil\n}\n\nfunc (s *inProcStream) CloseSend() error {\n\ts.closeOnce.Do(func() { close(s.send) })\n\treturn nil\n}\n","sourceCodeStart":33,"sourceCodeEnd":60,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/scc/inprocstream.go#L33-L60","documentation":"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.","triggerScenarios":"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().","commonSituations":"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.","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"],"exampleFix":"// before\nfor {\n  msg, err := stream.Recv()\n  if err != nil { log.Fatal(err) }\n  handle(msg)\n}\n// after\nfor {\n  msg, err := stream.Recv()\n  if err != nil {\n    if err.Error() == \"channel is closed\" { return } // normal shutdown\n    log.Fatal(err)\n  }\n  handle(msg)\n}","handlingStrategy":"try-catch","validationCode":"// Go: check liveness before consuming\nselect {\ncase msg, ok := <-stream.RecvChan(): // if such access is available\n    if !ok { return } // closed\ndefault:\n}","typeGuard":null,"tryCatchPattern":"// Go\nmsg, err := stream.Recv()\nif err != nil {\n    if err.Error() == \"channel is closed\" {\n        return nil // expected termination, stop loop gracefully\n    }\n    return fmt.Errorf(\"recv failed: %w\", err)\n}","preventionTips":["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"],"tags":["hyperledger-fabric","golang","channel-closed","stream"],"backgroundTag":"channel-closed","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}