hyperledger/fabric · error

no block header

Error message

no block header

What it means

The builtin validation plugin validates block.Header before evaluating transactions, since header data (number, data hash) is needed by downstream validators. It returns 'no block header' when block.Header is nil, meaning the block lacks the header required for validation.

Source

Thrown at core/handlers/validation/builtin/default_validation.go:64

}

func (v *DefaultValidation) Validate(block *common.Block, namespace string, txPosition int, actionPosition int, contextData ...validation.ContextDatum) error {
	if len(contextData) == 0 {
		logger.Panicf("Expected to receive policy bytes in context data")
	}

	serializedPolicy, isSerializedPolicy := contextData[0].(vp.SerializedPolicy)
	if !isSerializedPolicy {
		logger.Panicf("Expected to receive a serialized policy in the first context data")
	}
	if block == nil || block.Data == nil {
		return errors.New("empty block")
	}
	if txPosition >= len(block.Data.Data) {
		return errors.Errorf("block has only %d transactions, but requested tx at position %d", len(block.Data.Data), txPosition)
	}
	if block.Header == nil {
		return errors.Errorf("no block header")
	}

	var err error
	switch {
	case v.Capabilities.V2_0Validation():
		err = v.TxValidatorV2_0.Validate(block, namespace, txPosition, actionPosition, serializedPolicy.Bytes())

	case v.Capabilities.V1_3Validation():
		err = v.TxValidatorV1_3.Validate(block, namespace, txPosition, actionPosition, serializedPolicy.Bytes())

	case v.Capabilities.V1_2Validation():
		fallthrough

	default:
		err = v.TxValidatorV1_2.Validate(block, namespace, txPosition, actionPosition, serializedPolicy.Bytes())
	}

	logger.Debugf("block %d, namespace: %s, tx %d validation results is: %v", block.Header.Number, namespace, txPosition, err)

View on GitHub (pinned to 2736b63f8f)

Solutions

  1. Ensure every block passed to Validate has a non-nil common.BlockHeader with Number and DataHash set
  2. Check block storage health; rebuild ledger databases (peer node rebuild-dbs) if blocks come back partially formed
  3. Fix the test harness or custom block source to populate the header
  4. If unmarshaling blocks, verify the protobuf payload decodes fully and log unmarshal errors instead of ignoring them

Example fix

// before
block := &common.Block{Data: &common.BlockData{Data: [][]byte{env}}}
err := v.Validate(block, ns, 0, 0, policy) // no block header
// after
block := &common.Block{
    Header: &common.BlockHeader{Number: 5, DataHash: dataHash},
    Data:   &common.BlockData{Data: [][]byte{env}},
}
err := v.Validate(block, ns, 0, 0, policy)
Defensive patterns

Strategy: validation

Validate before calling

func ensureHeader(b *common.Block) error {
    if b == nil || b.Header == nil {
        return errors.New("block missing header; cannot validate")
    }
    if len(b.Header.DataHash) == 0 {
        return errors.New("block header has empty DataHash")
    }
    return nil
}

Type guard

func hasHeader(b *common.Block) bool {
    return b != nil && b.Header != nil && len(b.Header.DataHash) > 0
}

Try / catch

if err := v.Validate(block, ns, txPos, actionPos, policy); err != nil {
    if err.Error() == "no block header" {
        logger.Errorf("block %d arrived without header; check storage", num)
    }
    return err
}

Prevention

When it happens

Trigger: Validate (committer path, TestErrorConversion, TestValidateBadInput) is called with a block whose Header field is nil — fabricated blocks in tests, or a deserialization/storage failure that dropped the header.

Common situations: Test fixtures constructing &common.Block{} without a Header; corrupted block store returning partial blocks after crash; custom block producers omitting the header; protobuf unmarshal errors silently yielding empty header fields.

Related errors


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