hyperledger/fabric · error
could not unmarshal CombinedPrincipal from principal
Error message
could not unmarshal CombinedPrincipal from principal
What it means
collectPrincipals wraps proto.Unmarshal failures when deserializing the principal.Principal payload into a CombinedPrincipal message, producing 'could not unmarshal CombinedPrincipal from principal: <cause>'. The principal was classified as COMBINED, but its opaque bytes are not a valid protobuf-encoded CombinedPrincipal.
Source
Thrown at msp/mspimpl.go:460
return err
}
}
return nil
}
// collectPrincipals collects principals from combined principals into a single MSPPrincipal slice.
func collectPrincipals(principal *m.MSPPrincipal, mspVersion MSPVersion) ([]*m.MSPPrincipal, error) {
switch principal.PrincipalClassification {
case m.MSPPrincipal_COMBINED:
// Combined principals are not supported in MSP v1.0 or v1.1
if mspVersion <= MSPv1_1 {
return nil, errors.Errorf("invalid principal type %d", int32(principal.PrincipalClassification))
}
// Principal is a combination of multiple principals.
principals := &m.CombinedPrincipal{}
err := proto.Unmarshal(principal.Principal, principals)
if err != nil {
return nil, errors.Wrap(err, "could not unmarshal CombinedPrincipal from principal")
}
// Return an error if there are no principals in the combined principal.
if len(principals.Principals) == 0 {
return nil, errors.New("No principals in CombinedPrincipal")
}
// Recursively call msp.collectPrincipals for all combined principals.
// There is no limit for the levels of nesting for the combined principals.
var principalsSlice []*m.MSPPrincipal
for _, cp := range principals.Principals {
internalSlice, err := collectPrincipals(cp, mspVersion)
if err != nil {
return nil, err
}
principalsSlice = append(principalsSlice, internalSlice...)
}
// All the combined principals have been collected into principalsSlice
return principalsSlice, nil
default:View on GitHub (pinned to 2736b63f8f)
Solutions
- Inspect the wrapped cause; regenerate the principal via proto.Marshal on a properly populated &m.CombinedPrincipal{Principals: [...]}
- Verify the SDK/API used to build the policy constructs CombinedPrincipal, not another MSPPrincipal payload type
- Re-export/recreate the policy from the original toolchain instead of hand-editing serialized bytes
- Validate round-trip: proto.Unmarshal the payload before submitting the policy
Example fix
// before
principal := &m.MSPPrincipal{PrincipalClassification: m.MSPPrincipal_COMBINED, Principal: roleBytes} // wrong payload
// after
inner := &m.MSPPrincipal{PrincipalClassification: m.MSPPrincipal_ROLE,
Principal: proto.MarshalTextString(...) /* properly marshaled MSPPrincipal */}
cpBytes, _ := proto.Marshal(&m.CombinedPrincipal{Principals: []*m.MSPPrincipal{inner, inner2}})
principal := &m.MSPPrincipal{PrincipalClassification: m.MSPPrincipal_COMBINED, Principal: cpBytes} Defensive patterns
Strategy: validation
Validate before calling
cp := &m.CombinedPrincipal{}
if err := proto.Unmarshal(principal.Principal, cp); err != nil {
return fmt.Errorf("principal payload is not a valid CombinedPrincipal: %w", err)
}
// proceed with the MSP call only after this passes Type guard
func isValidCombinedPrincipalPayload(b []byte) bool {
cp := &m.CombinedPrincipal{}
return proto.Unmarshal(b, cp) == nil
} Try / catch
ok, err := msp.SatisfiesPrincipal(id, principal)
if err != nil {
if strings.Contains(err.Error(), "could not unmarshal CombinedPrincipal") {
log.Errorf("malformed COMBINED principal payload: %v", err)
}
return err
} Prevention
- Build COMBINED principals only via proto.Marshal(&m.CombinedPrincipal{Principals: [...]}) — never hand-pack bytes
- Confirm the inner Principal bytes are marshaled MSPPrincipal messages, not MSPRole/OrganizationUnit payloads
- Re-generate policies from the original SDK/toolchain instead of editing serialized policy blobs
- Round-trip test: Unmarshal the serialized principal before embedding it in a policy
When it happens
Trigger: Calling SatisfiesPrincipal/collectPrincipals with an MSPPrincipal whose PrincipalClassification is COMBINED but whose Principal bytes were built incorrectly — hand-marshaled bytes, bytes from a different message type (e.g. an MSPRole or OrganizationUnit), corrupted in transit, or an empty payload that fails unmarshal in the caller's proto version.
Common situations: Fabric SDK or tooling marshaling the wrong proto message into the COMBINED principal; policy files edited/re-serialized by external tools; byte corruption or truncation during policy transmission; mixing protobuf implementations with incompatible wire bytes.
Understand the failure class
Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.
Related errors
- failed to deserialize values
- error converting envelope to config update: %s
- failed unmarshaling identity %s
- failed to unmarshal response for transaction %s
- unmarshalling ChaincodeQueryResponse failed
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/bde1f46023b2a042.
Report an issue: GitHub.