hyperledger/fabric · error
invalid version in ChannelHeader. Expected 0, got [%d]
Error message
invalid version in ChannelHeader. Expected 0, got [%d]
What it means
The parsed ChannelHeader has a non-zero Version field. Fabric endorser transactions use version 0 for the channel header; a non-zero version means the header was constructed incorrectly or by tooling using a different versioning scheme. The parser rejects it to guarantee the transaction matches Fabric's expected format.
Source
Thrown at core/tx/endorser/parser.go:133
Response: ccAction.Response,
Events: ccAction.Events,
Results: ccAction.Results,
Endorsements: ccActionPayload.Action.Endorsements,
ChaincodeID: hdrExt.ChaincodeId,
Type: txenv.ChannelHeader.Type,
Version: txenv.ChannelHeader.Version,
Epoch: txenv.ChannelHeader.Epoch,
Nonce: txenv.SignatureHeader.Nonce,
}, nil
}
func (e *EndorserTx) validate() error {
if e.Epoch != 0 {
return errors.Errorf("invalid epoch in ChannelHeader. Expected 0, got [%d]", e.Epoch)
}
if e.Version != 0 {
return errors.Errorf("invalid version in ChannelHeader. Expected 0, got [%d]", e.Version)
}
if err := ValidateChannelID(e.ChannelID); err != nil {
return err
}
if len(e.Nonce) == 0 {
return errors.New("empty nonce")
}
if len(e.Creator) == 0 {
return errors.New("empty creator")
}
if e.ChaincodeID == nil {
return errors.New("nil ChaincodeId")
}
View on GitHub (pinned to 2736b63f8f)
Solutions
- Leave ChannelHeader.Version unset (zero value) when building the transaction header.
- Use protoutil.BuildChannelHeader to construct headers instead of manual structs.
- Check for header reuse across transaction types and create a fresh header for endorser txs.
Example fix
// before
ch := &common.ChannelHeader{ChannelId: "mychannel", Version: 1}
// after
ch := &common.ChannelHeader{ChannelId: "mychannel"} // Version zero value Defensive patterns
Strategy: validation
Validate before calling
func hasZeroVersion(ch *common.ChannelHeader) bool {
return ch.GetVersion() == 0
}
// check header before submission: if !hasZeroVersion(header) { fix header } Type guard
func isVersionZero(header *common.ChannelHeader) bool {
return header != nil && header.Version == 0
} Try / catch
tx, err := parser.UnmarshalEndorserTxAndValidate(env)
if err != nil {
if strings.Contains(err.Error(), "invalid version") {
// rebuild the header with version 0 and resubmit
return ErrInvalidHeader
}
return err
} Prevention
- Leave ChannelHeader.Version as the zero value for endorser transactions.
- Construct headers via protoutil.BuildChannelHeader.
- Review custom header builders after Fabric version upgrades.
When it happens
Trigger: UnmarshalEndorserTxAndValidate is given an envelope whose ChannelHeader.Version != 0 — e.g. a header built with Version set manually or reused from another header type (e.g. config updates that use versioning).
Common situations: Copying headers from config or block transactions that legitimately set Version; SDK/tooling that defaults Version to 1; version-skew after Fabric upgrades where custom builders picked wrong values.
Related errors
- nil ChannelHeader provided
- invalid Epoch in ChannelHeader. Expected 0, got [%d]
- invalid epoch in ChannelHeader. Expected 0, got [%d]
- '%s' not equal <newest|oldest|config|(number)>
- unmarshalling block: %s
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/be2563d396aa17c4.
Report an issue: GitHub.