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

  1. 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.
  2. 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.
  3. 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.
  4. 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

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


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