hyperledger/fabric · error
unexpected response type %d for transaction %s
Error message
unexpected response type %d for transaction %s
What it means
processChaincodeExecutionResult switches on the response ChaincodeMessage_Type and only handles COMPLETED and ERROR. Any other type (e.g. REGISTERED, READY, PUT_STATE, GET_STATE, INVOKE_CHAINCODE arriving where a completion was expected) yields this error. It indicates a protocol-level desynchronization: the peer expected a final transaction result but got an intermediate or unrelated message.
Source
Thrown at core/chaincode/chaincode_support.go:199
if resp.ChaincodeEvent != nil {
resp.ChaincodeEvent.ChaincodeId = ccName
resp.ChaincodeEvent.TxId = txid
}
switch resp.Type {
case pb.ChaincodeMessage_COMPLETED:
res := &pb.Response{}
err := proto.Unmarshal(resp.Payload, res)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to unmarshal response for transaction %s", txid)
}
return res, resp.ChaincodeEvent, nil
case pb.ChaincodeMessage_ERROR:
return nil, resp.ChaincodeEvent, errors.Errorf("transaction returned with failure: %s", resp.Payload)
default:
return nil, nil, errors.Errorf("unexpected response type %d for transaction %s", resp.Type, txid)
}
}
// Invoke will invoke chaincode and return the message containing the response.
// The chaincode will be launched if it is not already running.
func (cs *ChaincodeSupport) Invoke(txParams *ccprovider.TransactionParams, chaincodeName string, input *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) {
ccid, cctype, err := cs.CheckInvocation(txParams, chaincodeName, input)
if err != nil {
return nil, errors.WithMessage(err, "invalid invocation")
}
h, err := cs.Launch(ccid)
if err != nil {
return nil, err
}
return cs.execute(cctype, txParams, chaincodeName, input, h)
}View on GitHub (pinned to 2736b63f8f)
Solutions
- Log resp.Type for the failing txid and check chaincode container logs to see which message flow went wrong.
- Ensure peer and chaincode (shim) Fabric versions are compatible; rebuild/redeploy the chaincode against the peer's protos.
- Fix chaincode logic so every invocation path ends by returning (which makes the shim send COMPLETED/ERROR).
- Remove or correct any custom decorations/middleware that inject or replay chaincode messages.
- Retry the transaction after restart if handler state was corrupted; persistent occurrence means a real code/protocol bug.
Example fix
// before: chaincode function returns early without going through shim
func (c *CC) Bad(stub shim.ChaincodeStubInterface) pb.Response {
stub.PutState(...)
// missing return -> unexpected terminal message
}
// after
func (c *CC) Bad(stub shim.ChaincodeStubInterface) pb.Response {
if err := stub.PutState("k", []byte("v")); err != nil {
return shim.Error(err.Error())
}
return shim.Success(nil)
} Defensive patterns
Strategy: try-catch
Validate before calling
// sanity-check terminal message before processing
terminal := msg.Type == pb.ChaincodeMessage_COMPLETED || msg.Type == pb.ChaincodeMessage_ERROR
if !terminal {
return fmt.Errorf("handler delivered non-terminal message type %v for tx %s", msg.Type, txid)
} Type guard
func isTerminalChaincodeMessage(msg *pb.ChaincodeMessage) bool {
return msg != nil &&
(msg.Type == pb.ChaincodeMessage_COMPLETED || msg.Type == pb.ChaincodeMessage_ERROR)
} Try / catch
_, _, err := chaincodeSupport.Execute(txParams, ccName, input)
if err != nil {
if strings.Contains(err.Error(), "unexpected response type") {
// protocol desync: restart handler/chaincode container, verify versions, retry
log.Printf("protocol mismatch for tx %s: %v", txParams.TxID, err)
return rebuildChaincodeAndRetry(txParams, ccName)
}
return err
} Prevention
- Keep peer and chaincode (shim) versions protocol-compatible; rebuild chaincode on peer upgrades.
- Ensure every chaincode function ends with a return of shim.Success/shim.Error.
- Avoid custom message decorators/middleware unless thoroughly tested against the Fabric protocol.
- Enable core.chaincode logging at DEBUG to trace the message handshake when diagnosing.
When it happens
Trigger: The chaincode handler delivers a message type other than COMPLETED/ERROR as the terminal response of cs.execute — e.g. the chaincode asked the peer to perform state operations (GET_STATE/PUT_STATE) but never sent a final COMPLETED within expectations, or a mixed-version shim/peer misunderstanding produced an unexpected message type. Reached via ChaincodeSupport.Execute / ExecuteLegacyInit.
Common situations: Mixed Fabric v1.x chaincode with v2.x peer protocol mismatches; buggy custom shim or decorator altering message flow; chaincode that returns without completing the transaction; corrupted handler state after timeouts/retries.
Related errors
- First message needs to be a register
- Message is neither a Submit nor Consensus request
- malformed org definition for org: %s
- organization %s not found
- error encode input
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/e3fb7fd05b8c8915.
Report an issue: GitHub.