{"record":{"id":"02a6498a2b5cf709","repo":"grpc/grpc-go","slug":"fault-incorrect-config-type-provided-t-v","errorCode":null,"errorMessage":"fault: incorrect config type provided (%T): %v","messagePattern":"fault: incorrect config type provided \\(%T\\): (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/xds/httpfilter/fault/fault.go","lineNumber":125,"sourceCode":"\nfunc (builder) BuildClientFilter(httpfilter.ClientFilterOptions) httpfilter.ClientFilter {\n\treturn clientFilter{}\n}\n\nvar _ httpfilter.ClientFilterBuilder = builder{}\n\ntype clientFilter struct{}\n\nfunc (clientFilter) Close() {}\n\nfunc (clientFilter) BuildClientInterceptor(cfg, override httpfilter.FilterConfig) (httpfilter.ClientInterceptor, error) {\n\tif cfg == nil {\n\t\treturn nil, fmt.Errorf(\"fault: nil config provided\")\n\t}\n\n\tc, ok := cfg.(config)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"fault: incorrect config type provided (%T): %v\", cfg, cfg)\n\t}\n\n\tif override != nil {\n\t\t// override completely replaces the listener configuration; but we\n\t\t// still validate the listener config type.\n\t\tc, ok = override.(config)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"fault: incorrect override config type provided (%T): %v\", override, override)\n\t\t}\n\t}\n\n\ticfg := c.config\n\tif (icfg.GetMaxActiveFaults() != nil && icfg.GetMaxActiveFaults().GetValue() == 0) ||\n\t\t(icfg.GetDelay() == nil && icfg.GetAbort() == nil) {\n\t\treturn nil, nil\n\t}\n\treturn &interceptor{config: icfg}, nil\n}","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/xds/httpfilter/fault/fault.go#L107-L143","documentation":"BuildClientInterceptor asserts the base config is the internal fault config type produced by parseConfig (fault.go:123). A different concrete type means the build step received a config not created by this builder, indicating a wiring mismatch.","triggerScenarios":"cfg passed to BuildClientInterceptor does not implement/assert as the unexported fault config struct (e.g. it is the raw *fpb.HTTPFault or a config from another filter).","commonSituations":"Mixing filter builders and configs across filters; a custom filter framework that re-wraps configs; refactor that changed the config struct without updating the build path.","solutions":["Pass only the httpfilter.FilterConfig returned by the fault builder's ParseFilterConfig into BuildClientInterceptor.","Do not reconstruct or re-type the config between parse and build.","Add a build-time check that the builder and config originate from the same package."],"exampleFix":"// before: passing a raw *fpb.HTTPFault\nic, err := cf.BuildClientInterceptor(rawHTTPFault, nil)\n\n// after: pass the FilterConfig produced by the fault builder\nparsed, err := faultBuilder.ParseFilterConfig(anyMsg)\nic, err := cf.BuildClientInterceptor(parsed, nil)","handlingStrategy":"type-guard","validationCode":"// cfg must come from the fault builder's ParseFilterConfig\n// (the concrete type is unexported, so never reconstruct it by hand)","typeGuard":"// Internal type; callers can only assert it round-tripped through ParseFilterConfig:\nfunc isFaultConfig(c httpfilter.FilterConfig) bool {\n    // no public way to name the unexported config; rely on builder pairing\n    return c != nil\n}","tryCatchPattern":"ic, err := cf.BuildClientInterceptor(cfg, override)\nif err != nil {\n    return err\n}","preventionTips":["Use the same builder for parse and build; do not re-type configs across filters.","Avoid wrapping the config in a generic adapter between stages.","Test parse+build end-to-end per filter."],"tags":["fault-injection","grpc","xds","config","type-mismatch","go"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}