grpc/grpc-go · error
failed to unmarshal policy: %v
Error message
failed to unmarshal policy: %v
What it means
Raised by translatePolicy when the top-level policy JSON cannot be decoded. The decoder uses DisallowUnknownFields, so this fires both on malformed JSON AND on any field name not in the authorizationPolicy schema. The wrapped %v is the encoding/json error identifying the offending token/position.
Source
Thrown at authz/rbac_translator.go:367
case v3rbacpb.RBAC_AuditLoggingOptions_ON_ALLOW:
return v3rbacpb.RBAC_AuditLoggingOptions_NONE
case v3rbacpb.RBAC_AuditLoggingOptions_ON_DENY_AND_ALLOW:
return v3rbacpb.RBAC_AuditLoggingOptions_ON_DENY
default:
return v3rbacpb.RBAC_AuditLoggingOptions_NONE
}
}
// translatePolicy translates SDK authorization policy in JSON format to two
// Envoy RBAC polices (deny followed by allow policy) or only one Envoy RBAC
// allow policy. Also returns the overall policy name. If the input policy
// cannot be parsed or is invalid, an error will be returned.
func translatePolicy(policyStr string) ([]*v3rbacpb.RBAC, string, error) {
policy := &authorizationPolicy{}
d := json.NewDecoder(bytes.NewReader([]byte(policyStr)))
d.DisallowUnknownFields()
if err := d.Decode(policy); err != nil {
return nil, "", fmt.Errorf("failed to unmarshal policy: %v", err)
}
if policy.Name == "" {
return nil, "", fmt.Errorf(`"name" is not present`)
}
if len(policy.AllowRules) == 0 {
return nil, "", fmt.Errorf(`"allow_rules" is not present`)
}
allowLogger, denyLogger, err := policy.AuditLoggingOptions.toProtos()
if err != nil {
return nil, "", err
}
rbacs := make([]*v3rbacpb.RBAC, 0, 2)
if len(policy.DenyRules) > 0 {
denyPolicies, err := parseRules(policy.DenyRules, policy.Name)
if err != nil {
return nil, "", fmt.Errorf(`"deny_rules" %v`, err)
}
denyRBAC := &v3rbacpb.RBAC{View on GitHub (pinned to 03255a9237)
Solutions
- Run the policy string through `jq .` to confirm it is valid JSON.
- Compare top-level keys against the SDK's authorizationPolicy struct (Name, AllowRules, DenyRules, AuditLoggingOptions) — DisallowUnknownFields rejects anything else.
- Upgrade or align grpc-go and the authz SDK versions so the schema matches your policy.
- Wrap NewStaticInterceptors in a test that surfaces this error before runtime.
Example fix
// before (unknown field + trailing comma):
{ "name": "p", "allow_rule": [...] } // 'allow_rule' unknown; trailing comma
// after:
{ "name": "p", "allow_rules": [ { "name": "a", "request": { "paths": ["/x"] } } ] } Defensive patterns
Strategy: validation
Validate before calling
// Strict decode mirroring translatePolicy's DisallowUnknownFields + JSON
// validity, surfacing the exact offending token before the SDK rejects it.
func validatePolicyJSON(policyStr string) error {
type authzPolicy struct {
Name string `json:"name"`
AllowRules json.RawMessage `json:"allow_rules"`
DenyRules json.RawMessage `json:"deny_rules"`
AuditLoggingOptions json.RawMessage `json:"audit_logging_options"`
}
d := json.NewDecoder(strings.NewReader(policyStr))
d.DisallowUnknownFields()
var p authzPolicy
if err := d.Decode(&p); err != nil {
return fmt.Errorf("failed to unmarshal policy: %v", err)
}
return nil
} Prevention
- Run `jq .` on the policy before deploy.
- Mirror DisallowUnknownFields in your own pre-flight decoder.
- Keep field names lower_snake_case per the SDK schema.
- Version-pin the SDK and align policy fields accordingly.
When it happens
Trigger: Calling the authz SDK with a policy string that is invalid JSON, has a trailing comma, or contains an unknown top-level field (e.g. a typo like "allow_rule" instead of "allow_rules", or a field from a different schema version).
Common situations: YAML-to-JSON conversion producing trailing commas or comments; copy-pasting a field name from an Envoy RBAC config that doesn't exist in the gRPC SDK schema; using a field from a newer authz version on an older grpc-go; mismatched casing (AllowRules vs allow_rules).
Related errors
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/8aede689e0727949.
Report an issue: GitHub.