hyperledger/fabric · error

inner header is nil

Error message

inner header is nil

What it means

After unmarshaling the LastUpdate envelope's payload, the verifier requires a Payload.Header. A nil header means the inner transaction payload is malformed — the channel ID and type cannot be verified — so the config update is rejected.

Source

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

	if configOptions.LeaderRotation == smartbft.Options_ROTATION_ON {
		return fmt.Errorf("leader rotation must be turned off for this version or be unspecified")
	}

	return nil
}

func (cbv *ConfigBlockValidator) verifyConfigUpdateMsg(outEnv *common.Envelope, confEnv *common.ConfigEnvelope, chdr *common.ChannelHeader) error {
	if confEnv == nil || confEnv.LastUpdate == nil || confEnv.Config == nil {
		return errors.New("invalid config envelope")
	}
	envPayload, err := protoutil.UnmarshalPayload(confEnv.LastUpdate.Payload)
	if err != nil {
		return err
	}

	if envPayload.Header == nil {
		return errors.New("inner header is nil")
	}

	if envPayload.Header.ChannelHeader == nil {
		return errors.New("inner channelheader is nil")
	}

	typ := common.HeaderType(chdr.Type)

	cbv.Logger.Infof("Applying filters for config update of type %s to channel %s", typ, chdr.ChannelId)

	// First apply the filters on the outer envelope, regardless of the type of transaction it is.
	if err := cbv.Filters.ApplyFilters(chdr.ChannelId, outEnv); err != nil {
		return err
	}

	var expectedConfigEnv *common.ConfigEnvelope
	channelID, err := protoutil.ChannelID(confEnv.LastUpdate)
	if err != nil {

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Populate Payload.Header with a valid common.Header (including ChannelHeader) when building the config update envelope
  2. Build the update with SDK/tooling that constructs the full header chain instead of manual assembly
  3. Re-fetch a known-good config update envelope and redo the modification

Example fix

// before
payload := &common.Payload{Data: data} // Header missing
// after
payload := &common.Payload{Header: &common.Header{ChannelHeader: chdrBytes, SignatureHeader: shdrBytes}, Data: data}
Defensive patterns

Strategy: type-guard

Validate before calling

if envPayload == nil || envPayload.Header == nil {
    return errors.New("inner payload header is missing")
}

Type guard

func hasPayloadHeader(p *common.Payload) bool {
    return p != nil && p.Header != nil
}

Prevention

When it happens

Trigger: confEnv.LastUpdate.Payload unmarshals but envPayload.Header == nil in verifyConfigUpdateMsg, typically because the payload was constructed without a Header.

Common situations: Custom-built config update transactions missing the payload header; corruption when copying an envelope from another channel; clients that marshal a Payload with only Data set.

Related errors


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