grpc/grpc-go · error

"headers" %d: "key" is not present

Error message

"headers" %d: "key" is not present

What it means

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.

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 03255a9237)

Solutions

  1. Add a non-empty "key" field to every entry in the policy's "headers" arrays.
  2. Validate the policy JSON with a schema before deploying it.
  3. Use the same field name consistently (key, not name/header) per the policy format.
  4. Find the entry at the reported index and fix or remove it.

Example fix

// before
{"name":[{"headers":[{"values":["xyz"]}]}]}

// after
{"name":[{"headers":[{"key":"x-custom","values":["xyz"]}]}]}
Defensive patterns

Strategy: validation

Validate before calling

for i, h := range policyHeaders {
    if h.Key == "" {
        return fmt.Errorf("headers[%d] missing key", i)
    }
}

Try / catch

interceptor, err := authz.NewStatic(policyJSON)
if err != nil {
    return fmt.Errorf("invalid authz policy: %w", err)
}

Prevention

When it happens

Trigger: An authorization policy whose "allow"/"deny" rules contain a "headers" array element without a "key" field, e.g. {"values":["x"]} with no key.

Common situations: Hand-writing policy JSON and omitting the key; a policy generator emitting incomplete header objects; truncation/copy-paste errors in the config.

Related errors


AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07). Data as JSON: /api/errors/bbc9e1c52b9019f5. Report an issue: GitHub.