{"record":{"id":"d12961801e100a81","repo":"grpc/grpc-go","slug":"extproc-header-mutation-disallowed-by-headermutat-d12961","errorCode":null,"errorMessage":"extproc: header mutation disallowed by headerMutationRules for header %q","messagePattern":"extproc: header mutation disallowed by headerMutationRules for header %q","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/httpfilter/extconfig.go","lineNumber":194,"sourceCode":"// The input metadata must not be nil.\nfunc (hmr *HeaderMutationRules) ApplyRemovals(headersToRemove []string, input metadata.MD) error {\n\tif hmr == nil {\n\t\thmr = &HeaderMutationRules{}\n\t}\n\tif input == nil {\n\t\treturn fmt.Errorf(\"extproc: input metadata is nil\")\n\t}\n\tif hmr.DisallowAll {\n\t\treturn nil\n\t}\n\n\tfor _, header := range headersToRemove {\n\t\tif len(header) == 0 || header[0] == ':' || header == \"host\" || header != strings.ToLower(header) || len(header) > 16384 {\n\t\t\tcontinue\n\t\t}\n\t\tif !hmr.allow(header) {\n\t\t\tif hmr.DisallowIsError {\n\t\t\t\treturn fmt.Errorf(\"extproc: header mutation disallowed by headerMutationRules for header %q\", header)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tinput.Delete(header)\n\t}\n\treturn nil\n}\n\nfunc (hmr *HeaderMutationRules) allow(key string) bool {\n\tif hmr.DisallowExpr != nil && hmr.DisallowExpr.MatchString(key) {\n\t\treturn false\n\t}\n\tif hmr.AllowExpr != nil && hmr.AllowExpr.MatchString(key) {\n\t\treturn true\n\t}\n\tif hmr.AllowExpr != nil {\n\t\treturn false\n\t}","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/httpfilter/extconfig.go#L176-L212","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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)."],"exampleFix":"// before\n//   mutation_rules: { allow_expression: { regex: \"x-.*\" }, disallow_is_error: { value: true } }\n//   // ext_proc removes \"authorization\" -> error 371\n//\n// after\n//   mutation_rules: { allow_expression: { regex: \"(x-.*|authorization)\" }, disallow_is_error: { value: true } }","handlingStrategy":"validation","validationCode":"// Pre-check that every header the processor removes is allowed (extconfig.go:192-195).\nfunc sanitizeRemovals(hmr *httpfilter.HeaderMutationRules, remove []string) error {\n    for _, k := range remove {\n        if k == \"\" || k != strings.ToLower(k) { continue }\n        if !headerAllowed(hmr, k) && hmr.DisallowIsError {\n            return fmt.Errorf(\"remove header %q is disallowed\", k)\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := hmr.ApplyRemovals(removeHeaders, md); err != nil {\n    // DisallowIsError path: surface the offending header (ext_proc.go:1380 -> failProcStream).\n    return status.Errorf(codes.Internal, \"header removal rejected: %v\", err)\n}","preventionTips":["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."],"tags":["grpc","xds","ext-proc","header-mutation","policy"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}