hyperledger/fabric · error
chaincode stream terminated
Error message
chaincode stream terminated
What it means
Returned by Handler.execute when the chaincode's gRPC stream terminates (h.streamDone() fires) while the peer is waiting for a transaction response. The handler creates ErrorStreamTerminated ('chaincode stream terminated') because the chaincode can no longer respond — its connection to the peer was closed.
Source
Thrown at core/chaincode/handler.go:1452
}
defer h.TXContexts.Delete(msg.ChannelId, msg.Txid)
if err = h.setChaincodeProposal(txParams.SignedProp, txParams.Proposal, msg); err != nil {
return nil, err
}
h.serialSendAsync(msg)
var ccresp *pb.ChaincodeMessage
select {
case ccresp = <-txctx.ResponseNotifier:
// response is sent to user or calling chaincode. ChaincodeMessage_ERROR
// are typically treated as error
case <-time.After(timeout):
err = errors.New(ErrorExecutionTimeout)
h.Metrics.ExecuteTimeouts.With("chaincode", h.chaincodeID).Add(1)
case <-h.streamDone():
err = errors.New(ErrorStreamTerminated)
}
return ccresp, err
}
func (h *Handler) setChaincodeProposal(signedProp *pb.SignedProposal, prop *pb.Proposal, msg *pb.ChaincodeMessage) error {
if prop != nil && signedProp == nil {
return errors.New("failed getting proposal context. Signed proposal is nil")
}
// TODO: This doesn't make a lot of sense. Feels like both are required or
// neither should be set. Check with a knowledgeable expert.
if prop != nil {
msg.Proposal = signedProp
}
return nil
}
func (h *Handler) getCollectionStore(channelID string) privdata.CollectionStore {View on GitHub (pinned to 2736b63f8f)
Solutions
- Inspect the chaincode container logs for panic/exit right before the error.
- Fix panics in the chaincode (add recover, validate inputs) so it never terminates mid-call.
- Check container resource limits (memory/CPU) that could kill the container.
- Verify network connectivity between peer and chaincode (docker network, k8s service) and retry the transaction.
Example fix
// chaincode: guard against panics killing the stream
// before
func (t *Chaincode) Invoke(stub shim.ChaincodeStubInterface) pb.Response {
return t.dispatch(stub)
}
// after
func (t *Chaincode) Invoke(stub shim.ChaincodeStubInterface) (resp pb.Response) {
defer func() {
if r := recover(); r != nil {
resp = shim.Error(fmt.Sprintf("panic: %v", r))
}
}()
return t.dispatch(stub)
} Defensive patterns
Strategy: try-catch
Try / catch
if err != nil && strings.Contains(err.Error(), "stream terminated") {
// chaincode crashed: inspect container logs, fix panic, resubmit transaction
log.Printf("chaincode stream died during tx %s: %v", txid, err)
} Prevention
- Add panic recovery in the chaincode Invoke entrypoint.
- Set adequate container memory/CPU limits to avoid OOM kills.
- Monitor chaincode container restart counts and peer logs for stream errors.
When it happens
Trigger: During execute's select, `case <-h.streamDone()` fires: the chaincode handler stream was closed (chaincode crashed, deregistered, container exited, network drop) before the transaction response arrived.
Common situations: Chaincode container crash/OOM mid-transaction; CCaaS build/launch failures; peer-to-chaincode network interruption; chaincode calling os.Exit or panicking during Invoke; peer shutting down.
Related errors
- Recv() error: %v, closing connection
- First message needs to be a register
- TLS is active but chaincode %s didn't send certificate
- Chaincode %s with given certificate hash %v not found in reg
- Chaincode %s with given certificate hash %v belongs to a dif
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/a4d7f687d7dfa3e1.
Report an issue: GitHub.