grpc/grpc-go · error

extauthz: error parsing override config %v: unknown type %T,

Error message

extauthz: error parsing override config %v: unknown type %T, want *anypb.Any

What it means

Raised by ParseFilterConfigOverride (ext_authz.go:172) when the per-route override message passed in is not an *anypb.Any. The builder contract (httpfilter.go:60) wraps typed filter configs in *anypb.Any, so a non-Any indicates the xDS decoding layer supplied the wrong concrete type for an ExtAuthzPerRoute override.

Source

Thrown at internal/xds/httpfilter/ext_authz/ext_authz.go:172

		failureModeAllowHeaderAdd:  msg.GetFailureModeAllowHeaderAdd(),
		statusOnError:              statusOnError,
		allowedHeaders:             allowedHeaders,
		disallowedHeaders:          disallowedHeaders,
		decoderHeaderMutationRules: mutationRules,
		includePeerCertificate:     msg.GetIncludePeerCertificate(),
	}, nil
}

// ParseFilterConfigOverride parses the provided override configuration.
//
// Note that ExtAuthzPerRoute is unmarshaled to verify its syntax during xDS
// resource validation, no filter configuration object is returned. Per-route
// disabling is supported via the generic FilterConfig wrapper mechanism rather
// than the ExtAuthzPerRoute.disabled field directly.
func (builder) ParseFilterConfigOverride(overrideCfg proto.Message) (httpfilter.FilterConfig, error) {
	m, ok := overrideCfg.(*anypb.Any)
	if !ok {
		return nil, fmt.Errorf("extauthz: error parsing override config %v: unknown type %T, want *anypb.Any", overrideCfg, overrideCfg)
	}
	msg := new(v3extauthzpb.ExtAuthzPerRoute)
	if err := m.UnmarshalTo(msg); err != nil {
		return nil, fmt.Errorf("extauthz: failed to unmarshal override config %v: %v", overrideCfg, err)
	}
	return nil, nil
}

func (builder) IsTerminal() bool {
	return false
}

View on GitHub (pinned to 03255a9237)

Solutions

  1. If you are calling ParseFilterConfigOverride directly (tests/tools), wrap your message with anypb.New before passing it.
  2. If this surfaces in production, it indicates a resolver/builder version mismatch — update gRPC-Go so the resolver and this filter package agree on the *anypb.Any contract.
  3. Confirm the per-route typed_per_filter_config entry uses the ExtAuthzPerRoute type URL so the resolver keeps it wrapped as an Any.

Example fix

// before (test / direct call): passing the raw message
//   fc, err := b.ParseFilterConfigOverride(perRoute) // perRoute is *v3extauthzpb.ExtAuthzPerRoute
//
// after: wrap in *anypb.Any
anyOv, err := anypb.New(perRoute)
if err != nil { return err }
fc, err := b.ParseFilterConfigOverride(anyOv)
Defensive patterns

Strategy: type-guard

Validate before calling

// Guarantee the override is wrapped as *anypb.Any before handing it to
// ParseFilterConfigOverride (ext_authz.go:170).
func wrapOverride(m proto.Message) (*anypb.Any, error) {
    if a, ok := m.(*anypb.Any); ok {
        return a, nil
    }
    return anypb.New(m)
}

Type guard

func isAny(m proto.Message) (*anypb.Any, bool) {
    a, ok := m.(*anypb.Any)
    return a, ok
}

Prevention

When it happens

Trigger: The xDS resolver calls builder.ParseFilterConfigOverride(overrideCfg) where overrideCfg is a proto.Message that is not *anypb.Any (the type assertion at ext_authz.go:170 fails). This is an internal/xDS-decoder contract violation rather than something an end user's YAML directly controls.

Common situations: A gRPC/xDS resolver change that passes a raw typed struct (e.g. TypedStruct or the already-unmarshaled ExtAuthzPerRoute) instead of the wrapping Any; a test harness that calls ParseFilterConfigOverride directly with the wrong type; version skew between the resolver and the filter builder.

Related errors


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