{"record":{"id":"16ea469ef7c41e96","repo":"hyperledger/fabric","slug":"missing-signature-header","errorCode":null,"errorMessage":"missing signature header","messagePattern":"missing signature header","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"core/ledger/snapshotgrpc/snapshot_service.go","lineNumber":112,"sourceCode":"\t\treturn nil, err\n\t}\n\n\tlgr, err := s.getLedger(query.ChannelId)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tresult, err := lgr.PendingSnapshotRequests()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &pb.QueryPendingSnapshotsResponse{BlockNumbers: result}, nil\n}\n\nfunc (s *SnapshotService) checkACL(resName string, signatureHdr *cb.SignatureHeader, signedRequest *pb.SignedSnapshotRequest) error {\n\tif signatureHdr == nil {\n\t\treturn errors.New(\"missing signature header\")\n\t}\n\n\texpirationTime := crypto.ExpiresAt(signatureHdr.Creator)\n\tif !expirationTime.IsZero() && time.Now().After(expirationTime) {\n\t\treturn errors.New(\"client identity expired\")\n\t}\n\n\tif err := s.ACLProvider.CheckACLNoChannel(\n\t\tresName,\n\t\t[]*protoutil.SignedData{{\n\t\t\tIdentity:  signatureHdr.Creator,\n\t\t\tData:      signedRequest.Request,\n\t\t\tSignature: signedRequest.Signature,\n\t\t}},\n\t); err != nil {\n\t\treturn err\n\t}\n","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/core/ledger/snapshotgrpc/snapshot_service.go#L94-L130","documentation":"checkACL requires a non-nil SignatureHeader to identify the requester and verify their signature. When the signature header inside the snapshot request is nil, the service cannot even attempt ACL validation and returns 'missing signature header' immediately.","triggerScenarios":"Submitting Generate/Cancel/QueryPendings with a request whose SnapshotRequest.SignatureHeader field is nil — an unpopulated creator/channel header — so checkACL receives signatureHdr == nil.","commonSituations":"Hand-built clients forgetting to populate SignatureHeader; marshaling a partially-initialized request that dropped the header; a client library upgrade changing header construction.","solutions":["Populate SignatureHeader (creator identity + channel ID) before marshaling and signing the request.","Use protoutil's signature-header helpers to build it consistently.","Verify the unmarshaled request actually contains the header after round-tripping through your transport.","Check client SDK version for regressions in header construction."],"exampleFix":"// before\nreq := &pb.SnapshotRequest{ChannelId: chID}\n// after\nreq := &pb.SnapshotRequest{ChannelId: chID, SignatureHeader: protoutil.NewSignatureHeader(signer)}","handlingStrategy":"validation","validationCode":"if req.GetSignatureHeader() == nil {\n    return fmt.Errorf(\"refusing to submit: SignatureHeader is nil\")\n}\nraw, _ := proto.Marshal(req)\nsignedReq := &pb.SignedSnapshotRequest{Request: raw, Signature: sig}\nerr := svc.Generate(ctx, signedReq)","typeGuard":"func hasSignatureHeader(r *pb.SnapshotRequest) bool {\n    return r != nil && r.GetSignatureHeader() != nil && len(r.GetSignatureHeader().Creator) > 0\n}","tryCatchPattern":"_, err := svc.Generate(ctx, signedReq)\nif err != nil && strings.Contains(err.Error(), \"missing signature header\") {\n    // rebuild request with protoutil.NewSignatureHeader(signer) and re-sign\n}\n","preventionTips":["Always construct requests through a helper that sets SignatureHeader.","Assert header presence after unmarshal round trips.","Review SDK upgrade notes for changes to header construction APIs."],"tags":["grpc","snapshot","acl","authentication"],"backgroundTag":"missing-signature-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"}