grpc/grpc-go · error
extproc: header mutation disallowed by headerMutationRules f
Error message
extproc: header mutation disallowed by headerMutationRules for header %q
What it means
Raised by (*HeaderMutationRules).ApplyRemovals (extconfig.go:194) when the external server requests removal of a header whose key is rejected by the allow/disallow rules and DisallowIsError is true. Symmetric to error 369 but for header removals: without DisallowIsError the removal is silently skipped (extconfig.go:196), with it the mutation becomes an error and fails the ext_proc stream.
Source
Thrown at internal/xds/httpfilter/extconfig.go:194
// The input metadata must not be nil.
func (hmr *HeaderMutationRules) ApplyRemovals(headersToRemove []string, input metadata.MD) error {
if hmr == nil {
hmr = &HeaderMutationRules{}
}
if input == nil {
return fmt.Errorf("extproc: input metadata is nil")
}
if hmr.DisallowAll {
return nil
}
for _, header := range headersToRemove {
if len(header) == 0 || header[0] == ':' || header == "host" || header != strings.ToLower(header) || len(header) > 16384 {
continue
}
if !hmr.allow(header) {
if hmr.DisallowIsError {
return fmt.Errorf("extproc: header mutation disallowed by headerMutationRules for header %q", header)
}
continue
}
input.Delete(header)
}
return nil
}
func (hmr *HeaderMutationRules) allow(key string) bool {
if hmr.DisallowExpr != nil && hmr.DisallowExpr.MatchString(key) {
return false
}
if hmr.AllowExpr != nil && hmr.AllowExpr.MatchString(key) {
return true
}
if hmr.AllowExpr != nil {
return false
}View on GitHub (pinned to 03255a9237)
Solutions
- Adjust the allow/disallow regex so the header name in %q is permitted for removal.
- Change the external processor to only remove allowed headers.
- If dropping that removal is acceptable, disable DisallowIsError (disallowed removals are then ignored instead of erroring).
Example fix
// before
// mutation_rules: { allow_expression: { regex: "x-.*" }, disallow_is_error: { value: true } }
// // ext_proc removes "authorization" -> error 371
//
// after
// mutation_rules: { allow_expression: { regex: "(x-.*|authorization)" }, disallow_is_error: { value: true } } Defensive patterns
Strategy: validation
Validate before calling
// Pre-check that every header the processor removes is allowed (extconfig.go:192-195).
func sanitizeRemovals(hmr *httpfilter.HeaderMutationRules, remove []string) error {
for _, k := range remove {
if k == "" || k != strings.ToLower(k) { continue }
if !headerAllowed(hmr, k) && hmr.DisallowIsError {
return fmt.Errorf("remove header %q is disallowed", k)
}
}
return nil
} Try / catch
if err := hmr.ApplyRemovals(removeHeaders, md); err != nil {
// DisallowIsError path: surface the offending header (ext_proc.go:1380 -> failProcStream).
return status.Errorf(codes.Internal, "header removal rejected: %v", err)
} Prevention
- Ensure the allow/disallow regex covers every header your processor removes.
- Turn on disallow_is_error only after verifying removal coverage.
- Have the external processor validate its removal list against the configured rules before responding.
When it happens
Trigger: The ext_proc server returns remove_headers containing key K; hmr.allow(K) is false at extconfig.go:192 and DisallowIsError is true, so the error is returned through applyMutations (ext_proc.go:1380).
Common situations: allow_expression/disallow_expression forbid the header the processor tries to delete while DisallowIsError is on; e.g. the server attempts to strip 'authorization' but the rules only allow 'x-' prefixed headers.
Related errors
- extproc: header mutation disallowed by headerMutationRules f
- extproc: input metadata is nil
- httpfilter: %v
- extproc: invalid request body mode %v: want %q or %q
- extproc: invalid response body mode %v: want %q or %q
AI-assisted analysis of grpc/grpc-go@03255a9237 (2026-08-07).
Data as JSON: /api/errors/d12961801e100a81.
Report an issue: GitHub.