{"record":{"id":"8a1fd56b6999b2a3","repo":"apache/pulsar","slug":"invalid-combination-of-original-principal-cannot-b","errorCode":null,"errorMessage":"Invalid combination of Original principal cannot be empty if the request is via proxy.","messagePattern":"Invalid combination of Original principal cannot be empty if the request is via proxy\\.","errorType":"http","errorClass":"RestException","httpStatus":401,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java","lineNumber":201,"sourceCode":"    public boolean isRequestHttps() {\n        return \"https\".equalsIgnoreCase(httpRequest.getScheme());\n    }\n\n    public String getWebServiceListenerName() {\n        if (httpRequest == null) {\n            return null;\n        }\n        return (String) httpRequest.getAttribute(WebService.ATTRIBUTE_LISTENER_NAME);\n    }\n\n    public static boolean isClientAuthenticated(String appId) {\n        return appId != null;\n    }\n\n    private void validateOriginalPrincipal(String authenticatedPrincipal, String originalPrincipal) {\n        if (!pulsar.getBrokerService().getAuthorizationService()\n                .isValidOriginalPrincipal(authenticatedPrincipal, originalPrincipal, clientAuthData())) {\n            throw new RestException(Status.UNAUTHORIZED,\n                    \"Invalid combination of Original principal cannot be empty if the request is via proxy.\");\n        }\n    }\n\n    protected boolean hasSuperUserAccess() {\n        try {\n            validateSuperUserAccess();\n        } catch (Exception e) {\n            return false;\n        }\n        return true;\n    }\n\n    public CompletableFuture<Void> validateSuperUserAccessAsync() {\n        if (!config().isAuthenticationEnabled() || !config().isAuthorizationEnabled()) {\n            return CompletableFuture.completedFuture(null);\n        }\n        String appId = clientAppId();","sourceCodeStart":183,"sourceCodeEnd":219,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java#L183-L219","documentation":"When a request arrives through a proxy, the authenticated principal is the proxy role and the real caller is conveyed as the original principal. validateOriginalPrincipal consults AuthorizationService.isValidOriginalPrincipal; if the combination is invalid — e.g. the original principal is empty/blank while the request comes via a proxy role — the admin resource rejects the call with HTTP 401 and this message.","triggerScenarios":"Calling an admin REST endpoint through a proxy without setting the X-Original-Principal header (or setting it empty); a client behind proxyRoles whose forwarded original principal is blank; misconfigured proxy that strips the original-principal header.","commonSituations":"Deployments fronting the broker with Pulsar proxy but clients not configured to pass original principal; custom gateways dropping the header; authentication enabled but proxy forwarding not set up.","solutions":["Configure the client/proxy to send the X-Original-Principal header with the real caller role","Verify the proxy is in broker's proxyRoles so the original-principal logic is applied","If not using a proxy, connect directly to the broker so no original principal is required","Check AuthorizationService.isValidOriginalPrincipal logic/implementation for your auth provider"],"exampleFix":"// before (proxy-forwarded request)\ncurl -H \"Authorization: Bearer <proxy-token>\" http://proxy:8080/admin/v2/clusters\n// after\ncurl -H \"Authorization: Bearer <proxy-token>\" -H \"X-Original-Principal: admin-client\" http://proxy:8080/admin/v2/clusters","handlingStrategy":"validation","validationCode":"// client-side: ensure original principal is forwarded when going through a proxy\nif (usingProxy && (originalPrincipal == null || originalPrincipal.isBlank())) {\n    throw new IllegalStateException(\"X-Original-Principal must be set when connecting through a proxy\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    admin.tenants().getTenant(tenant);\n} catch (PulsarAdminException e) {\n    if (e.getStatusCode() == 401) {\n        // check proxy/original-principal configuration\n    }\n    throw e;\n}","preventionTips":["Always set X-Original-Principal when clients connect via a proxy","Keep the proxy role listed in broker proxyRoles","Integration-test proxied admin calls in staging"],"tags":["authentication","authorization","proxy","rest-api","pulsar"],"backgroundTag":"missing-original-principal","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"}