grpc/grpc-go · error
extauthz: failed to unmarshal config: %v
Error message
extauthz: failed to unmarshal config: %v
What it means
Raised by the External Authorization HTTP filter's ParseFilterConfig (ext_authz.go:98) when the bytes inside an *anypb.Any cannot be deserialized into the envoy.extensions.filters.http.ext_authz.v3.ExtAuthz protobuf message. The filter refuses to build a configuration from structurally invalid wire-format data, so the xDS resource carrying this filter is rejected (NACKed) and the previously accepted config remains active.
Source
Thrown at internal/xds/httpfilter/ext_authz/ext_authz.go:99
return fraction{numerator: num, denominator: den}, nil
}
// grpcStatusCode converts an HTTP status code to a gRPC status code.
func grpcStatusCode(httpStatus int32) codes.Code {
if code, ok := transport.HTTPStatusConvTab[int(httpStatus)]; ok {
return code
}
return codes.Unknown
}
func (builder) ParseFilterConfig(cfg proto.Message) (httpfilter.FilterConfig, error) {
m, ok := cfg.(*anypb.Any)
if !ok {
return nil, fmt.Errorf("extauthz: error parsing config %v: unknown type %T, want *anypb.Any", cfg, cfg)
}
msg := new(v3extauthzpb.ExtAuthz)
if err := m.UnmarshalTo(msg); err != nil {
return nil, fmt.Errorf("extauthz: failed to unmarshal config: %v", err)
}
if msg.GetGrpcService() == nil {
return nil, fmt.Errorf("extauthz: empty grpc_service provided in config %v", cfg)
}
server, err := parseGRPCServiceConfig(msg.GetGrpcService())
if err != nil {
return nil, fmt.Errorf("extauthz: failed to parse grpc_service: %v", err)
}
filterEnabled, err := parseFilterEnabled(msg.GetFilterEnabled())
if err != nil {
return nil, err
}
var denyAtDisable bool
if denyAtDisableFlag := msg.GetDenyAtDisable(); denyAtDisableFlag != nil {
if denyAtDisableFlag.GetDefaultValue() == nil {View on GitHub (pinned to 03255a9237)
Solutions
- Read the wrapped %v: a protobuf 'invalid wire type'/'unexpected EOF' points to truncated/corrupt bytes, while a field-level error points to an unknown/wrong-typed field from version skew.
- Confirm the Any.type_url is exactly 'type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz' and that the payload was serialized from the same v3 ExtAuthz message.
- Decode the Any.value (base64) with protoc --decode against the v3 ExtAuthz descriptor to confirm the bytes are a valid ExtAuthz; if it decodes as another message, fix the control-plane template.
- Align the go-control-plane (and envoy proto) versions between your control plane and this gRPC client so the field set matches.
Example fix
// before: control plane emits an Any whose payload is NOT a v3 ExtAuthz
// type_url: type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
// value: <bytes serialized from a different/older message>
//
// after: marshal the real v3 ExtAuthz into the Any
import (
"google.golang.org/protobuf/types/known/anypb"
v3extauthzpb "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/http/ext_authz/v3"
)
cfg := &v3extauthzpb.ExtAuthz{
Services: &v3extauthzpb.ExtAuthz_GrpcService{ /* ... */ },
}
anyCfg, err := anypb.New(cfg) // type_url + payload always agree
if err != nil { return err } Defensive patterns
Strategy: validation
Validate before calling
// Validate an ExtAuthz Any before publishing it to the control plane
// (mirrors the check at ext_authz.go:97-99).
func validateExtAuthzAny(a *anypb.Any) error {
if a == nil || a.TypeUrl != "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz" {
return fmt.Errorf("wrong/nil type_url for ExtAuthz")
}
msg := new(v3extauthzpb.ExtAuthz)
if err := a.UnmarshalTo(msg); err != nil {
return fmt.Errorf("ExtAuthz payload invalid: %w", err)
}
return nil
} Type guard
// Narrow to *anypb.Any before parsing (ext_authz.go:93).
func asAny(m proto.Message) (*anypb.Any, bool) {
a, ok := m.(*anypb.Any)
return a, ok
} Prevention
- Always build typed filter configs with anypb.New so the type_url and payload can never disagree.
- Keep the go-control-plane (envoy proto) version on your control plane aligned with the gRPC client build.
- In CI, decode every xDS resource's Any.value with protoc against the declared descriptor before publishing.
When it happens
Trigger: The xDS resolver hands the HTTP filter's typed_config Any to builder.ParseFilterConfig; the type assertion to *anypb.Any succeeds, but m.UnmarshalTo(new(v3extauthzpb.ExtAuthz)) returns a non-nil error. This happens when the Any payload is truncated, corrupt, or was serialized from a different message type than its type_url claims.
Common situations: Control plane (Istio, Traffic Director, etc.) generated a malformed ExtAuthz config; a v2-vs-v3 proto version mismatch where the Any actually contains envoy.config.filter.http.ext_authz.v2; go-control-plane library version skew between the management server and the gRPC client; a hand-crafted xDS resource with mismatched type_url and payload.
Related errors
- extauthz: failed to unmarshal override config %v: %v
- rls_csp: error parsing config %v: %v
- extauthz: missing default_value in filter_enabled
- extauthz: empty grpc_service provided in config %v
- extauthz: failed to parse grpc_service: %v
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/390a82d837647671.
Report an issue: GitHub.