{"record":{"id":"46f9c74bf4138752","repo":"dotnet/yarp","slug":"a-non-empty-custommetadata-value-is-required-46f9c7","errorCode":null,"errorMessage":"A non-empty CustomMetadata value is required","messagePattern":"A non-empty CustomMetadata value is required","errorType":"validation","errorClass":"ArgumentException","httpStatus":null,"severity":"error","filePath":"testassets/ReverseProxy.Code/MyTransformProvider.cs","lineNumber":45,"sourceCode":"        // Check all clusters for a custom property and validate the associated transform data.\n        if (context.Cluster.Metadata?.TryGetValue(\"CustomMetadata\", out var value) ?? false)\n        {\n            if (string.IsNullOrEmpty(value))\n            {\n                context.Errors.Add(new ArgumentException(\"A non-empty CustomMetadata value is required\"));\n            }\n        }\n    }\n\n    public void Apply(TransformBuilderContext transformBuildContext)\n    {\n        // Check all routes for a custom property and add the associated transform.\n        if ((transformBuildContext.Route.Metadata?.TryGetValue(\"CustomMetadata\", out var value) ?? false)\n            || (transformBuildContext.Cluster?.Metadata?.TryGetValue(\"CustomMetadata\", out value) ?? false))\n        {\n            if (string.IsNullOrEmpty(value))\n            {\n                throw new ArgumentException(\"A non-empty CustomMetadata value is required\");\n            }\n\n            transformBuildContext.AddRequestTransform(transformContext =>\n            {\n                transformContext.ProxyRequest.Options.Set(new HttpRequestOptionsKey<string>(\"CustomMetadata\"), value);\n                return default;\n            });\n        }\n    }\n}\n","sourceCodeStart":27,"sourceCodeEnd":56,"githubUrl":"https://github.com/dotnet/yarp/blob/bd11867bee7df522e7fd3effb08a9c85fd616908/testassets/ReverseProxy.Code/MyTransformProvider.cs#L27-L56","documentation":"This is sample code in testassets/ReverseProxy.Code/MyTransformProvider, a custom ITransformProvider that reads a 'CustomMetadata' entry from route/cluster Metadata and stamps it onto the proxied request. Apply throws ArgumentException directly if the entry exists but its value is null or empty, because there is no meaningful header value to write. (The ValidateRoute/ValidateCluster overloads report the same condition via context.Errors instead of throwing.)","triggerScenarios":"Configuring a route or cluster with Metadata containing the key \"CustomMetadata\" mapped to an empty string (or null), then building/running the proxy so Apply executes during transform construction.","commonSituations":"Copying the sample and leaving CustomMetadata as \"\" in appsettings; environment-variable substitution that yields empty; JSON like \"CustomMetadata\": \"\"; a deployment that sets the metadata key but forgets the value.","solutions":["Set the CustomMetadata value to a non-empty string in the route/cluster Metadata.","Remove the CustomMetadata key entirely if the transform is not wanted.","If you copied this provider, relax the check or switch to reporting context.Errors in Apply-equivalent validation rather than throwing."],"exampleFix":"// before\n\"Metadata\": { \"CustomMetadata\": \"\" }\n\n// after\n\"Metadata\": { \"CustomMetadata\": \"my-value\" }","handlingStrategy":"validation","validationCode":"// In config loading, strip or reject empty CustomMetadata before the transform runs.\nforeach (var route in config.Routes) {\n    if (route.Metadata?.TryGetValue(\"CustomMetadata\", out var v) == true\n        && string.IsNullOrEmpty(v)) {\n        throw new InvalidOperationException($\"Route {route.RouteId} has empty CustomMetadata\");\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate metadata in a config validator (implement IConfigValidator) so empty values are rejected at startup with a clear error rather than at request time.","Keep CustomMetadata values in a central config source with schema validation.","If you adapt the sample, have Apply add to errors instead of throwing when possible."],"tags":["transforms","metadata","configuration","sample","yarp"],"backgroundTag":null,"analyzedSha":"bd11867bee7df522e7fd3effb08a9c85fd616908","analyzedAt":"2026-08-13T21:29:49.359Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}