{"record":{"id":"1df55a307c9838d5","repo":"hyperledger/fabric","slug":"channel-header-in-envelope-must-contain-timestamp","errorCode":null,"errorMessage":"channel header in envelope must contain timestamp","messagePattern":"channel header in envelope must contain timestamp","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"common/deliver/deliver.go","lineNumber":393,"sourceCode":"\t\treturn nil, nil, nil, err\n\t}\n\n\tshdr, err := protoutil.UnmarshalSignatureHeader(payload.Header.SignatureHeader)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\terr = h.validateChannelHeader(ctx, chdr)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\treturn payload, chdr, shdr, nil\n}\n\nfunc (h *Handler) validateChannelHeader(ctx context.Context, chdr *cb.ChannelHeader) error {\n\tif chdr.GetTimestamp() == nil {\n\t\terr := errors.New(\"channel header in envelope must contain timestamp\")\n\t\treturn err\n\t}\n\n\tenvTime := time.Unix(chdr.GetTimestamp().Seconds, int64(chdr.GetTimestamp().Nanos)).UTC()\n\tserverTime := time.Now()\n\n\tif math.Abs(float64(serverTime.UnixNano()-envTime.UnixNano())) > float64(h.TimeWindow.Nanoseconds()) {\n\t\terr := errors.Errorf(\"envelope timestamp %s is more than %s apart from current server time %s\", envTime, h.TimeWindow, serverTime)\n\t\treturn err\n\t}\n\n\terr := h.BindingInspector.Inspect(ctx, chdr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}","sourceCodeStart":375,"sourceCodeEnd":411,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/deliver/deliver.go#L375-L411","documentation":"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.","triggerScenarios":"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.","commonSituations":"Hand-rolled deliver clients that build ChannelHeader structs directly; SDK or tool versions that stopped auto-filling timestamps; test fixtures with minimal headers.","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."],"exampleFix":"// before\nchdr := &cb.ChannelHeader{Type: int32(cb.HeaderType_DELIVER_SEEK_INFO), ChannelId: chID}\n\n// after\nchdr := &cb.ChannelHeader{Type: int32(cb.HeaderType_DELIVER_SEEK_INFO), ChannelId: chID, Timestamp: protoutil.CurrentTimestampBytes()}","handlingStrategy":"validation","validationCode":"chdr, err := protoutil.UnmarshalChannelHeader(payload.Header.ChannelHeader)\nif err != nil || chdr.GetTimestamp() == nil {\n    return errors.New(\"channel header must carry a timestamp before sending to deliver\")\n}","typeGuard":"func hasTimestamp(chdr *cb.ChannelHeader) bool { return chdr != nil && chdr.GetTimestamp() != nil }","tryCatchPattern":"if err := h.validateChannelHeader(ctx, chdr); err != nil {\n    if strings.Contains(err.Error(), \"must contain timestamp\") {\n        // regenerate envelope with protoutil.CurrentTimestampBytes()\n    }\n    return err\n}","preventionTips":["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"],"tags":["hyperledger-fabric","deliver","timestamp"],"backgroundTag":"missing-required-header","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}