grpc/grpc-go · error

"headers" : "values" is not present

Error message

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

What it means

Returned by parseHeaders (rbac_translator.go:240) when a header entry has a key but its values array is empty or absent (len(header.Values) == 0). RBAC header matching requires at least one value to match against, so an empty values list is rejected at the given index.

Solutions

  1. Add at least one value to "values" for that header entry, e.g. ["Bearer ..."] or ["*"] for any value.
  2. Lint the policy JSON for header entries where values is missing or empty before deploy.

Example fix

// before
"headers": [ { "key": "authorization" } ]

// after
"headers": [ { "key": "authorization", "values": ["*"] } ]
Defensive patterns

Strategy: validation

Validate before calling

func validHeader(h struct{ Key string; Values []string }) error {
    if h.Key == "" || len(h.Values) == 0 {
        return errors.New("header requires key and at least one value")
    }
    return nil
}

Try / catch

interceptor, err := authz.NewStatic(policyJSON)
if err != nil {
    if strings.Contains(err.Error(), `"values" is not present`) {
        // add values to the flagged header entry and reload
    }
}

Prevention

When it happens

Trigger: A policy rule request.headers[] entry with "key" but no "values", or "values": []; e.g. {"key":"authorization"}.

Common situations: Authoring a header matcher and forgetting the values; a templating step that strips empty arrays; refactoring that moved values elsewhere.

Related errors


AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11). Data as JSON: /api/errors/580102bfa45840b1. Report an issue: GitHub.

Appendix: source

Thrown at authz/rbac_translator.go:240

	"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)))
	}
	if len(request.Headers) > 0 {
		headers, err := parseHeaders(request.Headers)
		if err != nil {
			return nil, err
		}
		and = append(and, permissionAnd(headers))

View on GitHub (pinned to 0c51461d27)