{"record":{"id":"3df81676a5e93961","repo":"dotnet/yarp","slug":"string-join-transformvalues-keys-3df816","errorCode":null,"errorMessage":"{string.Join(\";\", transformValues.Keys)}","messagePattern":"\\{string\\.Join\\(\";\", transformValues\\.Keys\\)\\}","errorType":"validation","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"src/ReverseProxy/Transforms/RequestHeadersTransformFactory.cs","lineNumber":112,"sourceCode":"            else\n            {\n                throw new ArgumentException($\"Unexpected parameters for RequestHeader: {string.Join(';', transformValues.Keys)}. Expected 'Set' or 'Append'\");\n            }\n        }\n        else if (transformValues.TryGetValue(RequestHeaderRouteValueKey, out var headerNameFromRoute))\n        {\n            TransformHelpers.CheckTooManyParameters(transformValues, expected: 2);\n            if (transformValues.TryGetValue(AppendKey, out var routeValueKeyAppend))\n            {\n                context.AddRequestHeaderRouteValue(headerNameFromRoute, routeValueKeyAppend, append: true);\n            }\n            else if (transformValues.TryGetValue(SetKey, out var routeValueKeySet))\n            {\n                context.AddRequestHeaderRouteValue(headerNameFromRoute, routeValueKeySet, append: false);\n            }\n            else\n            {\n                throw new NotSupportedException(string.Join(\";\", transformValues.Keys));\n            }\n        }\n        else if (transformValues.TryGetValue(RequestHeaderRemoveKey, out var removeHeaderName))\n        {\n            TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);\n            context.AddRequestHeaderRemove(removeHeaderName);\n        }\n        else if (transformValues.TryGetValue(RequestHeadersAllowedKey, out var allowedHeaders))\n        {\n            TransformHelpers.CheckTooManyParameters(transformValues, expected: 1);\n            var headersList = allowedHeaders.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);\n            context.AddRequestHeadersAllowed(headersList);\n        }\n        else\n        {\n            return false;\n        }\n","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/dotnet/yarp/blob/bd11867bee7df522e7fd3effb08a9c85fd616908/src/ReverseProxy/Transforms/RequestHeadersTransformFactory.cs#L94-L130","documentation":"Thrown by RequestHeadersTransformFactory.Build when a 'RequestHeaderRouteValue' transform key is present but neither 'Set' nor 'Append' is found. Notably this throws NotSupportedException with only the joined keys (no explanatory text), unlike the sibling RequestHeader branch which gives a descriptive ArgumentException — an internal inconsistency. Validate catches the same condition non-throwing at load time.","triggerScenarios":"A transform dictionary containing { \"RequestHeaderRouteValue\": \"X-Hdr\" } without Set/Append. Reaches Build via direct programmatic construction or when config validation is skipped.","commonSituations":"Missing Set/Append line in a config-driven route transform; typo'd sibling key; incomplete dynamically-generated transform.","solutions":["Add 'Set' or 'Append' with the route-value key, e.g. { \"RequestHeaderRouteValue\": \"X-Hdr\", \"Set\": \"userId\" }.","Fix typos in the key name (case-sensitive).","Ensure config validation runs at startup.","Validate the dictionary keys in code before calling Build."],"exampleFix":"// before\n{ \"RequestHeaderRouteValue\": \"X-Hdr\" }\n// after\n{ \"RequestHeaderRouteValue\": \"X-Hdr\", \"Set\": \"userId\" }","handlingStrategy":"validation","validationCode":"if (transformValues.ContainsKey(\"RequestHeaderRouteValue\")\n    && !transformValues.ContainsKey(\"Set\")\n    && !transformValues.ContainsKey(\"Append\"))\n{\n    throw new InvalidOperationException(\n        \"RequestHeaderRouteValue transform requires 'Set' or 'Append'.\");\n}","typeGuard":"static bool IsValidRequestHeaderRouteValueTransform(IReadOnlyDictionary<string, string> t)\n    => t.ContainsKey(\"RequestHeaderRouteValue\")\n       && (t.ContainsKey(\"Set\") || t.ContainsKey(\"Append\"));","tryCatchPattern":null,"preventionTips":["Enable startup config validation.","Validate dynamically generated transform dictionaries before registration.","Document the required Set/Append sibling for RequestHeaderRouteValue in your config templates."],"tags":["yarp","config","transforms","headers","routing"],"backgroundTag":null,"analyzedSha":"bd11867bee7df522e7fd3effb08a9c85fd616908","analyzedAt":"2026-08-13T21:29:49.359Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}