hyperledger/fabric · error
invalid header type %s
Error message
invalid header type %s
What it means
Validate() checks the proposal's channel header Type; only ENDORSER_TRANSACTION (and tolerated CONFIG reads in legacy paths) may reach the Propose API. Any other header type (e.g. CONFIG_UPDATE, TRANSACTION, QUERY) is rejected because proposals to the endorser are by definition endorsement requests.
Source
Thrown at core/endorser/msgvalidation.go:138
}, nil
}
func (up *UnpackedProposal) Validate(idDeserializer msp.IdentityDeserializer) error {
logger := decorateLogger(endorserLogger, &ccprovider.TransactionParams{
ChannelID: up.ChannelHeader.ChannelId,
TxID: up.TxID(),
})
// validate the header type
switch common.HeaderType(up.ChannelHeader.Type) {
case common.HeaderType_ENDORSER_TRANSACTION:
case common.HeaderType_CONFIG:
// The CONFIG transaction type has _no_ business coming to the propose API.
// In fact, anything coming to the Propose API is by definition an endorser
// transaction, so any other header type seems like it ought to be an error... oh well.
default:
return errors.Errorf("invalid header type %s", common.HeaderType(up.ChannelHeader.Type))
}
// ensure the epoch is 0
if up.ChannelHeader.Epoch != 0 {
return errors.Errorf("epoch is non-zero")
}
// ensure that there is a nonce
if len(up.SignatureHeader.Nonce) == 0 {
return errors.Errorf("nonce is empty")
}
// ensure that there is a creator
if len(up.SignatureHeader.Creator) == 0 {
return errors.New("creator is empty")
}
expectedTxID := protoutil.ComputeTxID(up.SignatureHeader.Nonce, up.SignatureHeader.Creator)View on GitHub (pinned to 2736b63f8f)
Solutions
- Ensure the proposal is built as an ENDORSER_TRANSACTION (ChannelHeader.Type = HeaderType_ENDORSER_TRANSACTION), which SDKs do automatically.
- Do not submit config-update or orderer envelopes to the endorser Propose API; use the orderer/system channel APIs instead.
- Check that custom protobuf assembly code sets the header type explicitly to 1 (ENDORSER_TRANSACTION).
Example fix
// before chdr.Type = int32(common.HeaderType_TRANSACTION) // after chdr.Type = int32(common.HeaderType_ENDORSER_TRANSACTION)
Defensive patterns
Strategy: validation
Validate before calling
if hdr.Type != int32(common.HeaderType_ENDORSER_TRANSACTION) {
return fmt.Errorf("only ENDORSER_TRANSACTION proposals allowed, got %d", hdr.Type)
} Type guard
func isEndorserTx(h *common.ChannelHeader) bool {
return h != nil && h.Type == int32(common.HeaderType_ENDORSER_TRANSACTION)
} Prevention
- Never feed orderer/config envelopes into the endorser API
- Set header type via SDK constants only
- Keep proposal construction in one audited code path
When it happens
Trigger: ProcessProposal -> preProcess -> Validate on a SignedProposal whose ChannelHeader.Type is not ENDORSER_TRANSACTION — e.g. reusing a config-update envelope or an already-committed transaction envelope as a proposal.
Common situations: Feeding orderer envelopes (BlockData transactions) back into the peer's ProcessProposal, misconfigured custom clients setting the header type manually, replaying fabric messages across the wrong APIs.
Related errors
- epoch is non-zero
- chaincode invocation spec did not contain chaincode spec
- chaincode input did not contain any input
- nonce is empty
- creator is empty
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/22c342cb46c8d473.
Report an issue: GitHub.