{"record":{"id":"b69462e32b2640e6","repo":"grpc/grpc-go","slug":"empty-prefix-is-not-allowed-in-stringmatcher","errorCode":null,"errorMessage":"empty prefix is not allowed in StringMatcher","messagePattern":"empty prefix is not allowed in StringMatcher","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/matcher/string_matcher.go","lineNumber":108,"sourceCode":"\treturn s\n}\n\n// StringMatcherFromProto is a helper function to create a StringMatcher from\n// the corresponding StringMatcher proto.\n//\n// Returns a non-nil error if matcherProto is invalid.\nfunc StringMatcherFromProto(matcherProto *v3matcherpb.StringMatcher) (StringMatcher, error) {\n\tif matcherProto == nil {\n\t\treturn StringMatcher{}, errors.New(\"input StringMatcher proto is nil\")\n\t}\n\n\tmatcher := StringMatcher{ignoreCase: matcherProto.GetIgnoreCase()}\n\tswitch mt := matcherProto.GetMatchPattern().(type) {\n\tcase *v3matcherpb.StringMatcher_Exact:\n\t\tmatcher.exactMatch = newStrPtr(&mt.Exact, matcher.ignoreCase)\n\tcase *v3matcherpb.StringMatcher_Prefix:\n\t\tif matcherProto.GetPrefix() == \"\" {\n\t\t\treturn StringMatcher{}, errors.New(\"empty prefix is not allowed in StringMatcher\")\n\t\t}\n\t\tmatcher.prefixMatch = newStrPtr(&mt.Prefix, matcher.ignoreCase)\n\tcase *v3matcherpb.StringMatcher_Suffix:\n\t\tif matcherProto.GetSuffix() == \"\" {\n\t\t\treturn StringMatcher{}, errors.New(\"empty suffix is not allowed in StringMatcher\")\n\t\t}\n\t\tmatcher.suffixMatch = newStrPtr(&mt.Suffix, matcher.ignoreCase)\n\tcase *v3matcherpb.StringMatcher_SafeRegex:\n\t\tregex := matcherProto.GetSafeRegex().GetRegex()\n\t\tre, err := CompileSafeRegex(regex)\n\t\tif err != nil {\n\t\t\treturn StringMatcher{}, fmt.Errorf(\"safe_regex matcher %q is invalid\", regex)\n\t\t}\n\t\tmatcher = NewRegexStringMatcher(re)\n\tcase *v3matcherpb.StringMatcher_Contains:\n\t\tif matcherProto.GetContains() == \"\" {\n\t\t\treturn StringMatcher{}, errors.New(\"empty contains is not allowed in StringMatcher\")\n\t\t}","sourceCodeStart":90,"sourceCodeEnd":126,"githubUrl":"https://github.com/grpc/grpc-go/blob/0c51461d27177d997e14c642fe18c11668fc09a3/internal/xds/matcher/string_matcher.go#L90-L126","documentation":"Inside StringMatcherFromProto (string_matcher.go:97), the Prefix variant of the StringMatcher oneof requires a non-empty prefix string. An empty prefix would match everything and is semantically meaningless (and ambiguous with a true catch-all), so line 107-108 rejects it.","triggerScenarios":"Triggered when an xDS config provides a StringMatcher with the prefix pattern set to the empty string (`prefix: \"\"`). Encountered while decoding header matchers, path matchers, or any field that compiles down to a StringMatcher.","commonSituations":"A control-plane policy that defaulted prefix to empty instead of omitting it; YAML config written as `prefix:` with no value; an Envoy config ported to gRPC where an empty prefix was tolerated; a templating bug that produces `prefix: \"\"`.","solutions":["Provide a non-empty prefix string in the StringMatcher config.","If a catch-all is intended, omit the matcher entirely (or use a different mechanism) instead of using an empty prefix.","Audit the upstream xDS/RBAC/route configuration for empty prefix values and remove or fill them."],"exampleFix":"// before\nsm, err := matcher.StringMatcherFromProto(&v3matcherpb.StringMatcher{\n    MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: \"\"},\n}) // err: empty prefix is not allowed\n\n// after\nsm, err := matcher.StringMatcherFromProto(&v3matcherpb.StringMatcher{\n    MatchPattern: &v3matcherpb.StringMatcher_Prefix{Prefix: \"/svc/a\"},\n})","handlingStrategy":"validation","validationCode":"func validateStringMatcherProto(p *v3matcherpb.StringMatcher) error {\n    if p == nil { return errors.New(\"nil StringMatcher\") }\n    if prefix := p.GetPrefix(); prefix == \"\" && fmt.Sprintf(\"%T\", p.GetMatchPattern()) == \"*matcher.StringMatcher_Prefix\" {\n        return errors.New(\"StringMatcher.prefix must not be empty\")\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["In your control-plane config generator, never default prefix to empty — omit the matcher if unused.","Add a policy linter that flags `prefix: \"\"` in RBAC/route configs.","Use explicit `omitempty` semantics in YAML/JSON templates so empty values disappear."],"tags":["grpc","xds","matcher","validation","prefix"],"backgroundTag":null,"analyzedSha":"0c51461d27177d997e14c642fe18c11668fc09a3","analyzedAt":"2026-08-11T14:49:15.055Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}