hyperledger/fabric · error
invalid epoch in ChannelHeader. Expected 0, got [%d]
Error message
invalid epoch in ChannelHeader. Expected 0, got [%d]
What it means
The parsed ChannelHeader has a non-zero Epoch field. Fabric endorser transactions must have Epoch set to 0; a non-zero epoch indicates the header was built incorrectly or by non-standard tooling. This parser enforces the Fabric spec requirement strictly.
Source
Thrown at core/tx/endorser/parser.go:129
return &EndorserTx{
ComputedTxID: computedTxID,
ChannelID: txenv.ChannelHeader.ChannelId,
Creator: txenv.SignatureHeader.Creator,
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")
}
View on GitHub (pinned to 2736b63f8f)
Solutions
- Leave ChannelHeader.Epoch unset (zero value) when building the transaction header.
- Regenerate the header with protoutil.BuildChannelHeader, which does not set epoch.
- If the envelope comes from an external system, have it produce epoch-0 headers.
Example fix
// before
ch := &common.ChannelHeader{ChannelId: "mychannel", Epoch: 3}
// after
ch := &common.ChannelHeader{ChannelId: "mychannel"} // Epoch zero value Defensive patterns
Strategy: validation
Validate before calling
func hasZeroEpoch(ch *common.ChannelHeader) bool {
return ch.GetEpoch() == 0
}
// check header before submission: if !hasZeroEpoch(header) { fix header } Type guard
func isEpochZero(header *common.ChannelHeader) bool {
return header != nil && header.Epoch == 0
} Try / catch
tx, err := parser.UnmarshalEndorserTxAndValidate(env)
if err != nil {
if strings.Contains(err.Error(), "invalid epoch") {
// rebuild the header with epoch 0 and resubmit
return ErrInvalidHeader
}
return err
} Prevention
- Never set Epoch when constructing endorser transaction headers.
- Use protoutil.BuildChannelHeader instead of manual struct literals.
- Do not copy headers from config/block transactions into endorser txs.
When it happens
Trigger: UnmarshalEndorserTxAndValidate is given an envelope whose ChannelHeader.Epoch != 0 — typically set manually when constructing the header or copied from a config/other header type.
Common situations: Custom transaction builders hardcoding an epoch value; headers copied from block/config transactions where epoch carries meaning; ported code from other Fabric components that populate Epoch.
Related errors
- nil ChannelHeader provided
- invalid Epoch in ChannelHeader. Expected 0, got [%d]
- invalid version 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/409b1c5a9a7fc791.
Report an issue: GitHub.