hyperledger/fabric · error
channel header in envelope must contain timestamp
Error message
channel header in envelope must contain timestamp
What it means
validateChannelHeader requires the channel header timestamp field to be present. The timestamp is used to reject replayed/stale deliver requests; if chdr.Timestamp is nil the request cannot be time-validated and is rejected.
Source
Thrown at common/deliver/deliver.go:393
return nil, nil, nil, err
}
shdr, err := protoutil.UnmarshalSignatureHeader(payload.Header.SignatureHeader)
if err != nil {
return nil, nil, nil, err
}
err = h.validateChannelHeader(ctx, chdr)
if err != nil {
return nil, nil, nil, err
}
return payload, chdr, shdr, nil
}
func (h *Handler) validateChannelHeader(ctx context.Context, chdr *cb.ChannelHeader) error {
if chdr.GetTimestamp() == nil {
err := errors.New("channel header in envelope must contain timestamp")
return err
}
envTime := time.Unix(chdr.GetTimestamp().Seconds, int64(chdr.GetTimestamp().Nanos)).UTC()
serverTime := time.Now()
if math.Abs(float64(serverTime.UnixNano()-envTime.UnixNano())) > float64(h.TimeWindow.Nanoseconds()) {
err := errors.Errorf("envelope timestamp %s is more than %s apart from current server time %s", envTime, h.TimeWindow, serverTime)
return err
}
err := h.BindingInspector.Inspect(ctx, chdr)
if err != nil {
return err
}
return nil
}View on GitHub (pinned to 2736b63f8f)
Solutions
- Set Timestamp: protoutil.CurrentTimestampBytes or googleprotobuf Timestamp via proto now() when constructing the ChannelHeader.
- Regenerate the envelope with an SDK/helper (e.g. protoutil.CreateEnvelope) that fills the timestamp automatically.
- If encountering a third-party client, upgrade it to a version that sets the timestamp.
Example fix
// before
chdr := &cb.ChannelHeader{Type: int32(cb.HeaderType_DELIVER_SEEK_INFO), ChannelId: chID}
// after
chdr := &cb.ChannelHeader{Type: int32(cb.HeaderType_DELIVER_SEEK_INFO), ChannelId: chID, Timestamp: protoutil.CurrentTimestampBytes()} Defensive patterns
Strategy: validation
Validate before calling
chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)
if err != nil || chdr.GetTimestamp() == nil {
return errors.New("channel header must carry a timestamp before sending to deliver")
} Type guard
func hasTimestamp(chdr *cb.ChannelHeader) bool { return chdr != nil && chdr.GetTimestamp() != nil } Try / catch
if err := h.validateChannelHeader(ctx, chdr); err != nil {
if strings.Contains(err.Error(), "must contain timestamp") {
// regenerate envelope with protoutil.CurrentTimestampBytes()
}
return err
} Prevention
- Use protoutil.CurrentTimestampBytes() when constructing ChannelHeader structs
- Prefer protoutil.CreateEnvelope over manual header assembly
- Add a client-side assertion that Timestamp is non-nil before signing
When it happens
Trigger: An envelope passes payload/header unmarshalling (parseEnvelope) but the ChannelHeader was built without setting Timestamp (e.g. manual construction of cb.ChannelHeader with only Type and ChannelId) before being sent to the deliver service.
Common situations: Hand-rolled deliver clients that build ChannelHeader structs directly; SDK or tool versions that stopped auto-filling timestamps; test fixtures with minimal headers.
Related errors
- envelope timestamp %s is more than %s apart from current ser
- envelope has no header
- wrong chain type
- error getting private data by block number %d
- channel %s not found
AI-assisted analysis of hyperledger/fabric@2736b63f8f (2026-09-04).
Data as JSON: /api/errors/1df55a307c9838d5.
Report an issue: GitHub.