{"record":{"id":"bbc9e1c52b9019f5","repo":"grpc/grpc-go","slug":"headers-d-key-is-not-present","errorCode":null,"errorMessage":"\"headers\" %d: \"key\" is not present","messagePattern":"\"headers\" (.+?): \"key\" is not present","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"authz/rbac_translator.go","lineNumber":233,"sourceCode":"\t\"connection\":          true,\n\t\"keep-alive\":          true,\n\t\"proxy-authenticate\":  true,\n\t\"proxy-authorization\": true,\n\t\"te\":                  true,\n\t\"trailer\":             true,\n\t\"transfer-encoding\":   true,\n\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)))","sourceCodeStart":215,"sourceCodeEnd":251,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/authz/rbac_translator.go#L215-L251","documentation":"Returned by parseHeaders in the authz RBAC translator (rbac_translator.go:233) when a header matcher entry in the authorization policy JSON has no \"key\" field. Every header-based permission rule requires a header name to match against, so a missing key is rejected. The %d is the index of the offending entry.","triggerScenarios":"An authorization policy whose \"allow\"/\"deny\" rules contain a \"headers\" array element without a \"key\" field, e.g. {\"values\":[\"x\"]} with no key.","commonSituations":"Hand-writing policy JSON and omitting the key; a policy generator emitting incomplete header objects; truncation/copy-paste errors in the config.","solutions":["Add a non-empty \"key\" field to every entry in the policy's \"headers\" arrays.","Validate the policy JSON with a schema before deploying it.","Use the same field name consistently (key, not name/header) per the policy format.","Find the entry at the reported index and fix or remove it."],"exampleFix":"// before\n{\"name\":[{\"headers\":[{\"values\":[\"xyz\"]}]}]}\n\n// after\n{\"name\":[{\"headers\":[{\"key\":\"x-custom\",\"values\":[\"xyz\"]}]}]}","handlingStrategy":"validation","validationCode":"for i, h := range policyHeaders {\n    if h.Key == \"\" {\n        return fmt.Errorf(\"headers[%d] missing key\", 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":["Validate policy JSON against a schema before deploying.","Use codegen/templates to produce policy files consistently.","Check every headers entry has a key and values."],"tags":["go","grpc","authz","policy","rbac"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}