{"record":{"id":"19a1b345d3923026","repo":"grpc/grpc-go","slug":"headers-d-unsupported-key-s","errorCode":null,"errorMessage":"\"headers\" %d: unsupported \"key\" %s","messagePattern":"\"headers\" (.+?): unsupported \"key\" (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"authz/rbac_translator.go","lineNumber":237,"sourceCode":"\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)))\n\t}\n\tif len(request.Headers) > 0 {\n\t\theaders, err := parseHeaders(request.Headers)\n\t\tif err != nil {","sourceCodeStart":219,"sourceCodeEnd":255,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/authz/rbac_translator.go#L219-L255","documentation":"Returned by parseHeaders (rbac_translator.go:237) when a header matcher's key is unsupported: pseudo-headers (starting with ':'), 'grpc-' prefixed headers, or reserved HTTP/2 headers in the unsupportedHeaders set (connection, keep-alive, transfer-encoding, etc.). These headers cannot be matched by RBAC policy, so the translator rejects them.","triggerScenarios":"Writing a policy that tries to match on \":path\", \":method\", \"grpc-status\", \"content-length\", \"te\", \"host\", or any connection-specific header listed at rbac_translator.go:210-223.","commonSituations":"Attempting to route/authorize by pseudo-headers or gRPC metadata reserved names; copy-pasting Envoy header matchers that reference reserved headers.","solutions":["Remove or replace the unsupported header key with a permitted custom/application header.","To match the request path or method, use the dedicated \"paths\" policy field rather than the \":path\"/\":method\" pseudo-headers.","Cross-check the key against the unsupportedHeaders list and the ':' / 'grpc-' rules.","Use custom metadata (non-reserved) headers for authorization conditions."],"exampleFix":"// before\n{\"headers\":[{\"key\":\":path\",\"values\":[\"/svc/M\"]}]}\n\n// after - use the paths field instead\n{\"paths\":[{\"path\":\"/svc/M\"}]}","handlingStrategy":"validation","validationCode":"func isAllowedHeaderKey(k string) bool {\n    k = strings.ToLower(k)\n    return k[0] != ':' && !strings.HasPrefix(k, \"grpc-\") && !unsupportedHeaders[k]\n}","typeGuard":null,"tryCatchPattern":"interceptor, err := authz.NewStatic(policyJSON)\nif err != nil && strings.Contains(err.Error(), \"unsupported\") {\n    return fmt.Errorf(\"policy uses a reserved header: %w\", err)\n}","preventionTips":["Avoid pseudo-headers and grpc-* headers in matchers.","Use the paths field for path/method matching.","Keep a reference of unsupportedHeaders and screen keys against it."],"tags":["go","grpc","authz","policy","headers","rbac"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}