{"record":{"id":"ab391cbb34ba8eee","repo":"apache/pulsar","slug":"cluster-s-is-not-in-the-list-of-allowed-cluster","errorCode":null,"errorMessage":"Cluster [%s] is not in the list of allowed clusters list for tenant [%s]","messagePattern":"Cluster \\[(.+?)\\] is not in the list of allowed clusters list for tenant \\[(.+?)\\]","errorType":"http","errorClass":"RestException","httpStatus":403,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java","lineNumber":486,"sourceCode":"    protected void validateClusterForTenant(String tenant, String cluster) {\n        TenantInfo tenantInfo;\n        try {\n            tenantInfo = pulsar().getPulsarResources().getTenantResources().getTenant(tenant)\n                    .orElseThrow(() -> new RestException(Status.NOT_FOUND, \"Tenant does not exist\"));\n        } catch (RestException e) {\n            log.warn().attr(\"tenant\", tenant).log(\"Failed to get tenant admin data for tenant\");\n            throw e;\n        } catch (Exception e) {\n            log.error().attr(\"tenant\", tenant).exception(e).log(\"Failed to get tenant admin data for tenant\");\n            throw new RestException(e);\n        }\n\n        // Check if tenant is allowed on the cluster\n        if (!tenantInfo.getAllowedClusters().contains(cluster)) {\n            String msg = String.format(\"Cluster [%s] is not in the list of allowed clusters list for tenant [%s]\",\n                    cluster, tenant);\n            log.info(msg);\n            throw new RestException(Status.FORBIDDEN, msg);\n        }\n        log.info().attr(\"tenant\", tenant).log(\"Successfully validated clusters on tenant\");\n    }\n\n    protected CompletableFuture<Void> validateClusterForTenantAsync(String tenant, String cluster) {\n        return pulsar().getPulsarResources().getTenantResources().getTenantAsync(tenant)\n                .thenAccept(tenantInfo -> {\n                    if (tenantInfo.isEmpty()) {\n                        throw new RestException(Status.NOT_FOUND, \"Tenant does not exist\");\n                    }\n                    if (!tenantInfo.get().getAllowedClusters().contains(cluster)) {\n                        String msg = String.format(\"Cluster [%s] is not in the list of allowed clusters list\"\n                                        + \" for tenant [%s]\", cluster, tenant);\n                        log.info(msg);\n                        throw new RestException(Status.FORBIDDEN, msg);\n                    }\n                });\n    }","sourceCodeStart":468,"sourceCodeEnd":504,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/web/PulsarWebResource.java#L468-L504","documentation":"Each Pulsar tenant has an allowedClusters list. validateClusterForTenant checks that the cluster the operation targets (e.g. where a namespace is being created) is in that tenant's allowedClusters and throws 403 FORBIDDEN otherwise. It is a tenant-scoped cluster allowlist check, not a generic auth failure.","triggerScenarios":"Creating/updating a namespace or namespace-isolated policy under tenant T with cluster C where T's TenantInfo.allowedClusters does not contain C. Also hit when passing --cluster to namespace/tenant admin operations against a cluster the tenant was never provisioned on.","commonSituations":"Tenant created with default allowedClusters but admins operate against a new/renamed cluster; cluster renamed or added after tenant creation; multi-cluster setups where geo-replication clusters weren't added to the tenant; pointing pulsar-admin (service URL) at a different cluster than the tenant covers.","solutions":["Add the cluster to the tenant's allowed clusters: pulsar-admin tenants update <tenant> --allowed-clusters <c1,c2,...>","If the cluster shouldn't be used, retry the operation against a cluster already in the tenant's allowedClusters","Check the tenant's current config: pulsar-admin tenants get <tenant> and compare allowedClusters to the cluster in the failing request"],"exampleFix":"// before\npulsar-admin tenants create my-tenant --allowed-clusters cluster-a\n// after (allow cluster-b too)\npulsar-admin tenants update my-tenant --allowed-clusters cluster-a,cluster-b","handlingStrategy":"validation","validationCode":"// Ensure the target cluster is in the tenant's allowedClusters before the operation\nSet<String> allowed = admin.tenants().getTenant(tenant).getAllowedClusters();\nif (!allowed.contains(targetCluster)) {\n    admin.tenants().updateTenant(tenant,\n        new TenantInfoImpl(tenant.getAdminRoles(), append(allowed, targetCluster)));\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Add every cluster you operate on to the tenant's allowedClusters at provisioning","Re-verify allowedClusters after adding or renaming clusters","Use the tenant's allowedClusters to generate the namespace paths in automation"],"tags":["authorization","http-403","tenant","cluster"],"backgroundTag":"cluster-not-allowed-for-tenant","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"}