hyperledger/fabric · error

invalid type %s, expected %s

Error message

invalid type %s, expected %s

What it means

UnmarshalEnvelopeOfType unmarshals an envelope payload into a message and verifies the envelope's channel header type matches the expected header type. This error is thrown when the envelope's decoded header type (e.g. HEADER_TYPE_ENDORSER_TRANSACTION) does not match the headerType argument the caller requested. It is a type-mismatch guard, not a corruption error: the envelope parsed fine, it just isn't the kind of message the caller expected.

Source

Thrown at protoutil/commonutils.go:75

// UnmarshalEnvelopeOfType unmarshals an envelope of the specified type,
// including unmarshalling the payload data
func UnmarshalEnvelopeOfType(envelope *cb.Envelope, headerType cb.HeaderType, message proto.Message) (*cb.ChannelHeader, error) {
	payload, err := UnmarshalPayload(envelope.Payload)
	if err != nil {
		return nil, err
	}

	if payload.Header == nil {
		return nil, errors.New("envelope must have a Header")
	}

	chdr, err := UnmarshalChannelHeader(payload.Header.ChannelHeader)
	if err != nil {
		return nil, err
	}

	if chdr.Type != int32(headerType) {
		return nil, errors.Errorf("invalid type %s, expected %s", cb.HeaderType(chdr.Type), headerType)
	}

	err = proto.Unmarshal(payload.Data, message)
	err = errors.Wrapf(err, "error unmarshalling message for type %s", headerType)
	return chdr, err
}

// ExtractEnvelopeOrPanic retrieves the requested envelope from a given block
// and unmarshals it -- it panics if either of these operations fail
func ExtractEnvelopeOrPanic(block *cb.Block, index int) *cb.Envelope {
	envelope, err := ExtractEnvelope(block, index)
	if err != nil {
		panic(err)
	}
	return envelope
}

// ExtractEnvelope retrieves the requested envelope from a given block and

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Check the actual header type of your envelope: unmarshal the payload and inspect chdr.Type, then route it to the matching decoder instead of the expected one.
  2. Verify you are passing the correct cb.HeaderType constant to UnmarshalEnvelopeOfType for the data you actually have.
  3. If the envelope should be of the expected type, trace its producer (transaction proposal or config update) — the submitting component built the wrong payload type.
  4. For genesis/config blocks, ensure the block you feed to channelConfigFromBlock/extractChannelConfig is a config block (IsConfigBlock) not an ordinary block with transactions.

Example fix

// before
envelope, _ := ExtractEnvelope(block, 0)
var config cb.ConfigEnvelope
err := protoutil.UnmarshalEnvelopeOfType(envelope, cb.HeaderType_CONFIG, &config) // fails: envelope is a transaction
// after
chdr, _ := protoutil.ChannelHeader(envelope)
if cb.HeaderType(chdr.Type) == cb.HeaderType_CONFIG {
    err = protoutil.UnmarshalEnvelopeOfType(envelope, cb.HeaderType_CONFIG, &config)
} else {
    err = protoutil.UnmarshalEnvelopeOfType(envelope, cb.HeaderType_ENDORSER_TRANSACTION, &tx)
}
Defensive patterns

Strategy: validation

Validate before calling

func hasHeaderType(t *testing.T, env *cb.Envelope, want cb.HeaderType) bool {
    chdr, err := protoutil.ChannelHeader(env)
    if err != nil {
        return false
    }
    return cb.HeaderType(chdr.Type) == want
}
// call protoutil.UnmarshalEnvelopeOfType(env, want, msg) only when hasHeaderType(env, want)

Type guard

func envelopeHasType(env *cb.Envelope, want cb.HeaderType) bool {
    chdr, err := protoutil.ChannelHeader(env)
    return err == nil && cb.HeaderType(chdr.Type) == want
}

Try / catch

var msg cb.ConfigEnvelope
if err := protoutil.UnmarshalEnvelopeOfType(env, cb.HeaderType_CONFIG, &msg); err != nil {
    // inspect actual type and route accordingly
    chdr, herr := protoutil.ChannelHeader(env)
    if herr == nil {
        return fmt.Errorf("expected CONFIG, got %s: %w", cb.HeaderType(chdr.Type), err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling UnmarshalEnvelopeOfType with a specific cb.HeaderType (e.g. cb.HeaderType_CONFIG) against an envelope whose ChannelHeader.Type differs — e.g. passing an orderer config block to a function expecting a transaction, or feeding an ENDORSER_TRANSACTION envelope into a CONFIG decoder.

Common situations: Delivering blocks/transactions to the wrong processing path; peer/orderer code validating that a block actually contains a config transaction before applying it (ProcessConfigMsg, validateConfigBlock); fabric-sdk or external tooling submitting a payload of the wrong type for a channel; config updates sent as normal transactions.

Related errors


AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04). Data as JSON: /api/errors/7e2d3d54e42907a8. Report an issue: GitHub.