{"record":{"id":"580102bfa45840b1","repo":"grpc/grpc-go","slug":"headers-d-values-is-not-present","errorCode":null,"errorMessage":"\"headers\" %d: \"values\" is not present","messagePattern":"\"headers\" (.+?): \"values\" is not present","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"authz/rbac_translator.go","lineNumber":240,"sourceCode":"\t\"upgrade\":             true,\n}\n\nfunc unsupportedHeader(key string) bool {\n\treturn key[0] == ':' || strings.HasPrefix(key, \"grpc-\") || unsupportedHeaders[key]\n}\n\nfunc parseHeaders(headers []header) ([]*v3rbacpb.Permission, error) {\n\ths := make([]*v3rbacpb.Permission, 0, len(headers))\n\tfor i, header := range headers {\n\t\tif header.Key == \"\" {\n\t\t\treturn nil, fmt.Errorf(`\"headers\" %d: \"key\" is not present`, i)\n\t\t}\n\t\theader.Key = strings.ToLower(header.Key)\n\t\tif unsupportedHeader(header.Key) {\n\t\t\treturn nil, fmt.Errorf(`\"headers\" %d: unsupported \"key\" %s`, i, header.Key)\n\t\t}\n\t\tif len(header.Values) == 0 {\n\t\t\treturn nil, fmt.Errorf(`\"headers\" %d: \"values\" is not present`, i)\n\t\t}\n\t\tvalues := parseHeaderValues(header.Key, header.Values)\n\t\ths = append(hs, permissionOr(values))\n\t}\n\treturn hs, nil\n}\n\nfunc parseRequest(request request) (*v3rbacpb.Permission, error) {\n\tvar and []*v3rbacpb.Permission\n\tif len(request.Paths) > 0 {\n\t\tand = append(and, permissionOr(parsePaths(request.Paths)))\n\t}\n\tif len(request.Headers) > 0 {\n\t\theaders, err := parseHeaders(request.Headers)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tand = append(and, permissionAnd(headers))","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/authz/rbac_translator.go#L222-L258","documentation":"Returned by parseHeaders (rbac_translator.go:240) when a header matcher has a key but no \"values\" array (or an empty one). A header permission rule needs at least one value to match against, so a values-less entry is rejected. The %d is the offending entry index.","triggerScenarios":"A policy \"headers\" entry like {\"key\":\"x-foo\"} with no \"values\" field, or \"values\":[] with zero elements.","commonSituations":"Omitting values thinking the key alone matches presence; copy-paste truncation; policy generator dropping empty arrays.","solutions":["Add a non-empty \"values\" array to every header matcher entry.","If you want presence-based matching, list the expected value(s) or reconsider the rule structure.","Validate the policy JSON with a schema requiring values when key is present.","Inspect the entry at the reported index and fill in its values."],"exampleFix":"// before\n{\"headers\":[{\"key\":\"x-role\"}]}\n\n// after\n{\"headers\":[{\"key\":\"x-role\",\"values\":[\"admin\"]}]}","handlingStrategy":"validation","validationCode":"for i, h := range policyHeaders {\n    if len(h.Values) == 0 {\n        return fmt.Errorf(\"headers[%d] missing values\", i)\n    }\n}","typeGuard":null,"tryCatchPattern":"interceptor, err := authz.NewStatic(policyJSON)\nif err != nil {\n    return fmt.Errorf(\"invalid authz policy: %w\", err)\n}","preventionTips":["Always include a non-empty values array in header matchers.","Schema-validate the policy before deploy.","Review the header entry at the reported index when the error appears."],"tags":["go","grpc","authz","policy","rbac"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}