{"record":{"id":"f991fd94e9385a20","repo":"apache/pulsar","slug":"httpreverseproxy-s-proxyto-must-be-specified-exac","errorCode":null,"errorMessage":"httpReverseProxy.%s.proxyTo must be specified exactly once","messagePattern":"httpReverseProxy\\.(.+?)\\.proxyTo must be specified exactly once","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java","lineNumber":1120,"sourceCode":"    public void setProperties(Properties properties) {\n        this.properties = properties;\n\n        Map<String, Map<String, String>> redirects = new HashMap<>();\n        Pattern redirectPattern = Pattern.compile(\"^httpReverseProxy\\\\.([^\\\\.]*)\\\\.(.+)$\");\n        Map<String, List<Matcher>> groups = properties.stringPropertyNames().stream()\n            .map((s) -> redirectPattern.matcher(s))\n            .filter(Matcher::matches)\n            .collect(Collectors.groupingBy((m) -> m.group(1))); // group by name\n\n        groups.entrySet().forEach((e) -> {\n                Map<String, String> keyToFullKey = e.getValue().stream().collect(\n                        Collectors.toMap(m -> m.group(2), m -> m.group(0)));\n                if (!keyToFullKey.containsKey(\"path\")) {\n                    throw new IllegalArgumentException(\n                            String.format(\"httpReverseProxy.%s.path must be specified exactly once\", e.getKey()));\n                }\n                if (!keyToFullKey.containsKey(\"proxyTo\")) {\n                    throw new IllegalArgumentException(\n                            String.format(\"httpReverseProxy.%s.proxyTo must be specified exactly once\", e.getKey()));\n                }\n                httpReverseProxyConfigs.add(new HttpReverseProxyConfig(e.getKey(),\n                                                    properties.getProperty(keyToFullKey.get(\"path\")),\n                                                    properties.getProperty(keyToFullKey.get(\"proxyTo\"))));\n            });\n    }\n\n    public static class HttpReverseProxyConfig {\n        private final String name;\n        private final String path;\n        private final String proxyTo;\n\n        HttpReverseProxyConfig(String name, String path, String proxyTo) {\n            this.name = name;\n            this.path = path;\n            this.proxyTo = proxyTo;\n        }","sourceCodeStart":1102,"sourceCodeEnd":1138,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-proxy/src/main/java/org/apache/pulsar/proxy/server/ProxyConfiguration.java#L1102-L1138","documentation":"ProxyConfiguration.loadConfig validates that every httpReverseProxy.<name> block defines both a path and a proxyTo key exactly once. When a block omits httpReverseProxy.<name>.proxyTo (or path), the configuration cannot determine the upstream target the reverse proxy should forward to, so it refuses to start. This fail-fast validation prevents silently proxying to a null/undefined target.","triggerScenarios":"Calling ProxyConfiguration.loadConfig with properties that contain an httpReverseProxy.<name> block where httpReverseProxy.<name>.path is set but httpReverseProxy.<name>.proxyTo is missing (or vice versa); also triggered when the regex key extraction yields a group whose key-to-fullKey map lacks the 'proxyTo' entry, e.g. duplicate/malformed key names like httpReverseProxy.<name>.proxyTo defined with a typo such as proxyto.","commonSituations":"Hand-editing proxy config files and typo-ing 'proxyTo' as 'proxyto' or 'proxy_to'; copy-pasting a reverse-proxy block from docs and deleting the proxyTo line; leaving a stale block behind after removing an upstream service; migration from older configs that only had path.","solutions":["Add the missing key to the block, e.g. httpReverseProxy.<name>.proxyTo=http://localhost:8080","Verify the key spelling is exactly 'proxyTo' (camelCase, no underscores) and both path and proxyTo exist for the same <name>","Remove any orphaned/partial httpReverseProxy blocks for services no longer in use","Validate config in CI by constructing ProxyConfiguration and calling loadConfig on the file before deploy"],"exampleFix":"// before\nhttpReverseProxy.functions.path=/admin/functions\n\n// after\nhttpReverseProxy.functions.path=/admin/functions\nhttpReverseProxy.functions.proxyTo=http://localhost:6750","handlingStrategy":"validation","validationCode":"Properties props = loadProperties();\nSet<String> names = props.stringPropertyNames().stream()\n    .filter(k -> k.startsWith(\"httpReverseProxy.\"))\n    .map(k -> k.split(\"\\\\.\")[1])\n    .collect(Collectors.toSet());\nfor (String name : names) {\n    if (props.getProperty(\"httpReverseProxy.\" + name + \".path\") == null\n            || props.getProperty(\"httpReverseProxy.\" + name + \".proxyTo\") == null) {\n        throw new IllegalArgumentException(\"httpReverseProxy.\" + name + \" must define both path and proxyTo\");\n    }\n}","typeGuard":"boolean hasCompleteReverseProxyBlock(Properties props, String name) {\n    return props.getProperty(\"httpReverseProxy.\" + name + \".path\") != null\n        && props.getProperty(\"httpReverseProxy.\" + name + \".proxyTo\") != null;\n}","tryCatchPattern":"try {\n    config.loadConfig(props);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"httpReverseProxy\")) {\n        LOG.error(\"Invalid reverse proxy block, check path/proxyTo keys: {}\", e.getMessage());\n    }\n    throw e;\n}","preventionTips":["Always define path and proxyTo as a pair for each httpReverseProxy.<name> block","Use exact camelCase key names (proxyTo, not proxyto)","Lint proxy config files in CI by loading them with ProxyConfiguration before deploy","Delete stale reverse-proxy blocks when removing upstream services"],"tags":["pulsar-proxy","configuration","reverse-proxy","validation"],"backgroundTag":"missing-required-config-key","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}