hyperledger/fabric · error

unexpected envelope type %s

Error message

unexpected envelope type %s

What it means

ValidateConfig only accepts envelopes whose channel header type is HeaderType_CONFIG. Any other header type (e.g. TRANSACTION, CONFIG_UPDATE, DELIVER_SEEK_INFO) falls into the default case and is rejected with 'unexpected envelope type <name>'. This guards the SmartBFT config block validation path so only committed config blocks are processed.

Source

Thrown at orderer/consensus/smartbft/configverifier.go:90

	if payload.Header.ChannelHeader == nil {
		return fmt.Errorf("no channel header was set")
	}

	chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
	if err != nil {
		return fmt.Errorf("channel header unmarshalling error: %s", err)
	}

	switch chdr.Type {
	case int32(common.HeaderType_CONFIG):
		configEnvelope := &common.ConfigEnvelope{}
		if err = proto.Unmarshal(payload.Data, configEnvelope); err != nil {
			return fmt.Errorf("data unmarshalling error: %s", err)
		}
		return cbv.verifyConfigUpdateMsg(envelope, configEnvelope, chdr)
	default:
		return errors.Errorf("unexpected envelope type %s", common.HeaderType_name[chdr.Type])
	}
}

func (cbv *ConfigBlockValidator) checkConsentersMatchPolicy(conf *common.Config) error {
	if conf == nil {
		return fmt.Errorf("empty Config")
	}

	if conf.ChannelGroup == nil {
		return fmt.Errorf("empty channel group")
	}

	if len(conf.ChannelGroup.Groups) == 0 {
		return fmt.Errorf("no groups in channel group")
	}

	if conf.ChannelGroup.Groups["Orderer"] == nil {
		return fmt.Errorf("no 'Orderer' group in channel groups")

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Pass the committed CONFIG block's envelope (header type CONFIG), not the CONFIG_UPDATE submission envelope
  2. Verify payload.Header.ChannelHeader.Type is set to common.HeaderType_CONFIG before calling ValidateConfig
  3. If handling a config update submission, first run it through the orderer's broadcast/ProposeConfigUpdate flow to produce a CONFIG envelope
  4. Regenerate the envelope via protoutil (CreateSignedEnvelope with common.HeaderType_CONFIG) if it was built manually

Example fix

// before
env.HeaderType = common.HeaderType_CONFIG_UPDATE
validator.ValidateConfig(env) // -> unexpected envelope type CONFIG_UPDATE
// after
env.HeaderType = common.HeaderType_CONFIG
validator.ValidateConfig(env) // OK
Defensive patterns

Strategy: validation

Validate before calling

func isConfigEnvelope(env *common.Envelope) bool {
  payload := &common.Payload{}
  if proto.Unmarshal(env.Payload, payload) != nil || payload.Header == nil {
    return false
  }
  chdr := &common.ChannelHeader{}
  if proto.Unmarshal(payload.Header.ChannelHeader, chdr) != nil {
    return false
  }
  return chdr.Type == int32(common.HeaderType_CONFIG)
}

Type guard

func guardConfigType(chdr *common.ChannelHeader) bool {
  return chdr != nil && common.HeaderType(chdr.Type) == common.HeaderType_CONFIG
}

Try / catch

if err := cbv.ValidateConfig(envelope); err != nil {
  if strings.Contains(err.Error(), "unexpected envelope type") {
    // wrong header type; route to the appropriate validator for this HeaderType
    return handleNonConfigEnvelope(envelope, err)
  }
  return err
}

Prevention

When it happens

Trigger: Calling ConfigBlockValidator.ValidateConfig with an envelope whose ChannelHeader.Type is not int32(common.HeaderType_CONFIG) — e.g. passing a CONFIG_UPDATE transaction, an endorser transaction, or an envelope with a zero/unset (UNKNOWN) header type.

Common situations: Submitting a config update envelope (type CONFIG_UPDATE) instead of the resulting config block's envelope; feeding regular application transactions into the config validation path; constructing envelopes by hand and forgetting to set the channel header type; misconfigured deliver clients injecting non-config envelopes.

Related errors


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