hyperledger/fabric · error
unknown operation type
Error message
unknown operation type
What it means
HandleWriteBatch iterates over records in a WriteBatchState and switches on each record's Type (PUT_STATE, PUT_STATE_METADATA, etc.). A record whose type is not one of the known operation types hits the default branch and produces this error. It means the batch contains an unrecognized/unsupported write operation.
Source
Thrown at core/chaincode/handler.go:1331
if err = h.purgePrivateData(delState, txContext, msg.ChannelId); err != nil {
return nil, err
}
case pb.WriteRecord_PUT_STATE_METADATA:
putStateMetadata := &pb.PutStateMetadata{
Key: kv.GetKey(),
Collection: kv.GetCollection(),
Metadata: &pb.StateMetadata{
Metakey: kv.GetMetadata().GetMetakey(),
Value: kv.GetMetadata().GetValue(),
},
}
if err = h.putStateMetadata(putStateMetadata, txContext, msg.ChannelId); err != nil {
return nil, err
}
default:
return nil, errors.New("unknown operation type")
}
}
return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_RESPONSE, Txid: msg.Txid, ChannelId: msg.ChannelId}, nil
}
// Handles requests that modify ledger state
func (h *Handler) HandleInvokeChaincode(msg *pb.ChaincodeMessage, txContext *TransactionContext) (*pb.ChaincodeMessage, error) {
chaincodeLogger.Debugf("[%s] C-call-C", shorttxid(msg.Txid))
chaincodeSpec := &pb.ChaincodeSpec{}
err := proto.Unmarshal(msg.Payload, chaincodeSpec)
if err != nil {
return nil, errors.Wrap(err, "unmarshal failed")
}
// Get the chaincodeID to invoke. The chaincodeID to be called may
// contain composite info like "chaincode-name:version/channel-name".View on GitHub (pinned to 2736b63f8f)
Solutions
- Align peer and chaincode shim versions so all WriteRecord types are supported
- Make sure every WriteRecord added to the batch explicitly sets its Type field before sending
- Update fabric to a version where the new operation type is handled in HandleWriteBatch
Example fix
// before
rec := &pb.WriteRecord{Key: key, Value: value} // Type unset
// after
rec := &pb.WriteRecord{Type: pb.WriteRecord_PUT_STATE, Key: key, Value: value} Defensive patterns
Strategy: validation
Validate before calling
func validWriteType(t pb.WriteRecord_Type) bool {
switch t {
case pb.WriteRecord_PUT_STATE, pb.WriteRecord_PUT_STATE_METADATA:
return true
}
return false
}
// validate each record before adding to the batch Type guard
func isKnownWriteRecord(kv *pb.WriteRecord) bool {
return kv.GetType() == pb.WriteRecord_PUT_STATE ||
kv.GetType() == pb.WriteRecord_PUT_STATE_METADATA
} Try / catch
resp, err := handler.HandleWriteBatch(msg, txContext)
if err != nil {
if strings.Contains(err.Error(), "unknown operation type") {
// drop or quarantine the offending record; version-skew likely
return handleVersionSkew(msg)
}
return err
} Prevention
- Explicitly set the Type field on every pb.WriteRecord
- Upgrade peer and shim together so new WriteRecord types are understood by both
- Add unit tests covering every operation type included in write batches
When it happens
Trigger: A write batch containing a pb.WriteRecord with an unset or future/unknown Type value — e.g. a newer peer or shim emitting an operation type this handler version does not know, or a zero-valued WriteRecord that was never given a type.
Common situations: Version skew between peer and chaincode shim where a new batch operation type was added; custom shims constructing WriteRecord without setting Type; corrupted batch assembly that omits the type field.
Related errors
- signed chaincode deployment spec cannot be nil in a package
- invalid chaincode name: %q
- failed to unmarshal envelope from bytes
- error getting deployment spec
- error unmarshalling ChaincodeHeaderExtension
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/a638c8bfd26f7b8a.
Report an issue: GitHub.