grpc/grpc-go · error
"headers" : "key" is not present
Error message
"headers" %d: "key" is not present
What it means
Returned by parseHeaders (rbac_translator.go:233) while translating an authorization policy to Envoy RBAC. Each entry in a rule's request.headers must have a non-empty key; when header.Key == "" the parser reports the zero-based index i of the offending header. This becomes part of policy parsing, so it surfaces through NewStatic / file-watcher loading as a policy parse error.
Solutions
- Add a non-empty "key" to the header entry at the reported index in the policy JSON.
- Run the policy through a JSON schema/linter for the gRPC authorization policy before deploying.
- Use the file watcher's reload (it keeps the previous good policy) so a bad edit does not break a running server.
Example fix
// before
"headers": [ { "values": ["token"] } ]
// after
"headers": [ { "key": "authorization", "values": ["token"] } ] Defensive patterns
Strategy: validation
Validate before calling
// Validate a header matcher entry before relying on it.
func validHeader(h struct{ Key string; Values []string }) error {
if h.Key == "" {
return errors.New("header key required")
}
return nil
} Try / catch
interceptor, err := authz.NewStatic(policyJSON)
if err != nil {
if strings.Contains(err.Error(), `"key" is not present`) {
// add "key" to the flagged header entry and reload
}
} Prevention
- Require a non-empty key on every header matcher in policy authoring.
- Lint the policy JSON before deploying.
- Use file-watcher reload to keep the old policy while fixing a bad edit.
When it happens
Trigger: An authorization policy JSON where an element of allow_rules/deny_rules[].request.headers omits "key" or sets it to "", e.g. {"values":["x"]} with no key field.
Common situations: Hand-authored policy missing the key; templating that drops empty fields; copy-paste of a header block without the key.
Related errors
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/bbc9e1c52b9019f5.
Report an issue: GitHub.
Appendix: source
Thrown at authz/rbac_translator.go:233
"connection": true,
"keep-alive": true,
"proxy-authenticate": true,
"proxy-authorization": true,
"te": true,
"trailer": true,
"transfer-encoding": true,
"upgrade": true,
}
func unsupportedHeader(key string) bool {
return key[0] == ':' || strings.HasPrefix(key, "grpc-") || unsupportedHeaders[key]
}
func parseHeaders(headers []header) ([]*v3rbacpb.Permission, error) {
hs := make([]*v3rbacpb.Permission, 0, len(headers))
for i, header := range headers {
if header.Key == "" {
return nil, fmt.Errorf(`"headers" %d: "key" is not present`, i)
}
header.Key = strings.ToLower(header.Key)
if unsupportedHeader(header.Key) {
return nil, fmt.Errorf(`"headers" %d: unsupported "key" %s`, i, header.Key)
}
if len(header.Values) == 0 {
return nil, fmt.Errorf(`"headers" %d: "values" is not present`, i)
}
values := parseHeaderValues(header.Key, header.Values)
hs = append(hs, permissionOr(values))
}
return hs, nil
}
func parseRequest(request request) (*v3rbacpb.Permission, error) {
var and []*v3rbacpb.Permission
if len(request.Paths) > 0 {
and = append(and, permissionOr(parsePaths(request.Paths)))View on GitHub (pinned to 0c51461d27)