{"record":{"id":"ccd6561875bd7c82","repo":"dotnet/yarp","slug":"the-proxy-config-is-invalid","errorCode":null,"errorMessage":"The proxy config is invalid.","messagePattern":"The proxy config is invalid\\.","errorType":"exception","errorClass":"AggregateException","httpStatus":null,"severity":"critical","filePath":"src/ReverseProxy/Management/ProxyConfigManager.cs","lineNumber":490,"sourceCode":"            catch (ObjectDisposedException) { }\n        }\n\n        static void ReloadConfig(object? state)\n        {\n            var manager = (ProxyConfigManager)state!;\n            _ = manager.ReloadConfigAsync();\n        }\n    }\n\n    // Throws for validation failures\n    private async Task<bool> ApplyConfigAsync(IReadOnlyList<RouteConfig> routes, IReadOnlyList<ClusterConfig> clusters)\n    {\n        var (configuredClusters, clusterErrors) = await VerifyClustersAsync(clusters, cancellation: default);\n        var (configuredRoutes, routeErrors) = await VerifyRoutesAsync(routes, configuredClusters, cancellation: default);\n\n        if (routeErrors.Count > 0 || clusterErrors.Count > 0)\n        {\n            throw new AggregateException(\"The proxy config is invalid.\", routeErrors.Concat(clusterErrors));\n        }\n\n        // Update clusters first because routes need to reference them.\n        UpdateRuntimeClusters(configuredClusters);\n        var routesChanged = UpdateRuntimeRoutes(configuredRoutes);\n        return routesChanged;\n    }\n\n    private async Task<(IList<RouteConfig>, IList<Exception>)> VerifyRoutesAsync(IReadOnlyList<RouteConfig> routes, IReadOnlyDictionary<string, ClusterConfig> clusters, CancellationToken cancellation)\n    {\n        if (routes is null)\n        {\n            return (Array.Empty<RouteConfig>(), Array.Empty<Exception>());\n        }\n\n        var seenRouteIds = new HashSet<string>(routes.Count, StringComparer.OrdinalIgnoreCase);\n        var configuredRoutes = new List<RouteConfig>(routes.Count);\n        var errors = new List<Exception>();","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/dotnet/yarp/blob/bd11867bee7df522e7fd3effb08a9c85fd616908/src/ReverseProxy/Management/ProxyConfigManager.cs#L472-L508","documentation":"After loading all config, `ApplyConfigAsync` runs `VerifyClustersAsync` and `VerifyRoutesAsync` which use registered `IConfigValidator` instances to check every cluster and route. If any validation errors are collected, they are aggregated into a single `AggregateException` with this message. Each inner exception describes a specific validation failure (duplicate route IDs, invalid patterns, missing cluster references, etc.).","triggerScenarios":"Any registered `IConfigValidator` (built-in or custom) adds an exception to the errors list during validation. Common built-in validators check: route pattern validity, cluster ID references, transform validity, header matcher correctness, load-balancing policy names, and health check configuration. The error count being > 0 at line 488 triggers the AggregateException.","commonSituations":"A route references a `ClusterId` that doesn't exist in the clusters list. Two routes share the same `RouteId`. A route pattern is malformed. A transform is misconfigured (unknown transform name, missing required parameter). A cluster has an invalid load-balancing policy or health check endpoint configuration. Header match config has invalid mode/value combinations.","solutions":["Enumerate `aggregateException.InnerExceptions` to see every individual validation error with its specific message.","Fix each validation error in the configuration source (appsettings.json, in-memory config, or custom provider) — start with the first error as later errors may be cascading.","If using `appsettings.json`, validate the `ReverseProxy:Routes` and `ReverseProxy:Clusters` sections against the schema and YARP documentation.","For custom validators, ensure the validation logic matches your config expectations and the error messages are descriptive.","Use YARP's config validation at startup (it happens automatically) and fix all reported errors before deploying."],"exampleFix":"// Inspect the aggregate to find all validation errors\ntry\n{\n    builder.Services.AddReverseProxy().LoadFromConfig(config);\n    // ... app startup ...\n}\ncatch (InvalidOperationException ex) when (ex.InnerException is AggregateException agg)\n{\n    foreach (var error in agg.InnerExceptions)\n    {\n        Console.Error.WriteLine($\"Validation error: {error.Message}\");\n    }\n    // Fix each reported error in appsettings.json or config source\n    throw;\n}","handlingStrategy":"try-catch","validationCode":"// Validate routes and clusters before app startup\n// Manually run validators or parse config in a test:\nvar configText = File.ReadAllText(\"appsettings.json\");\nvar config = JsonSerializer.Deserialize<JsonElement>(configText);\n// Check for duplicate route IDs, missing cluster references, etc.","typeGuard":"// No type guard — config validation failures are content errors, not type errors.","tryCatchPattern":"try { builder.Services.AddReverseProxy().LoadFromConfig(config); }\ncatch (InvalidOperationException ex) when (ex.InnerException is AggregateException agg)\n{\n    foreach (var e in agg.InnerExceptions)\n        Console.Error.WriteLine($\"Validation: {e.Message}\");\n    throw;\n}","preventionTips":["Always enumerate the AggregateException.InnerExceptions to see every validation error at once.","Add a config-validation step in CI that loads and validates the config before deploying.","Keep route IDs unique and ensure every route's ClusterId references an existing cluster."],"tags":["configuration","validation","startup","routes","clusters"],"backgroundTag":null,"analyzedSha":"bd11867bee7df522e7fd3effb08a9c85fd616908","analyzedAt":"2026-08-13T21:29:49.359Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}