{"id":"cd30c7f7819acd65","repo":"apache/kafka","slug":"not-authorized-to-access-topics-unauthorizedtop-cd30c7","errorCode":null,"errorMessage":"Not authorized to access topics: ${unauthorizedTopics}","messagePattern":"Not authorized to access topics: (.+?)","errorType":"exception","errorClass":"TopicAuthorizationException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/TopicMetadataRequestManager.java","lineNumber":246,"sourceCode":"                Map<String, List<PartitionInfo>> res = handleTopicMetadataResponse((MetadataResponse) response.responseBody());\n                future.complete(res);\n                inflightRequests.remove(this);\n            } catch (Exception e) {\n                handleError(e, response.receivedTimeMs());\n            }\n        }\n\n        private void completeFutureAndRemoveRequest(final Throwable throwable) {\n            future.completeExceptionally(throwable);\n            inflightRequests.remove(this);\n        }\n\n        private Map<String, List<PartitionInfo>> handleTopicMetadataResponse(final MetadataResponse response) {\n            Cluster cluster = response.buildCluster();\n\n            final Set<String> unauthorizedTopics = cluster.unauthorizedTopics();\n            if (!unauthorizedTopics.isEmpty())\n                throw new TopicAuthorizationException(unauthorizedTopics);\n\n            Map<String, Errors> errors = response.errors();\n            if (!errors.isEmpty()) {\n                // if there were errors, we need to check whether they were fatal or whether\n                // we should just retry\n\n                log.debug(\"Topic metadata fetch included errors: {}\", errors);\n\n                for (Map.Entry<String, Errors> errorEntry : errors.entrySet()) {\n                    String topic = errorEntry.getKey();\n                    Errors error = errorEntry.getValue();\n\n                    if (error == Errors.INVALID_TOPIC_EXCEPTION)\n                        throw new InvalidTopicException(\"Topic '\" + topic + \"' is invalid\");\n                    else if (error == Errors.UNKNOWN_TOPIC_OR_PARTITION)\n                        // if a requested topic is unknown, we just continue and let it be absent\n                        // in the returned map\n                        continue;","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/TopicMetadataRequestManager.java#L228-L264","documentation":"Async-consumer counterpart of error 232. Thrown as TopicAuthorizationException by TopicMetadataRequestManager.TopicMetadataRequestHandler.handleTopicMetadataResponse when cluster.unauthorizedTopics() from the MetadataResponse is non-empty. The new consumer (broker-side network thread / RequestManager architecture) surfaces the same authorization failure as the legacy TopicMetadataFetcher, but from the async request pipeline.","triggerScenarios":"Any async-consumer metadata operation (partitionsFor, listTopics, subscribe that triggers metadata refresh) where the authenticated principal lacks the DESCRIBE ACL on at least one requested topic. Raised on the network thread and propagated via the request's CompletableFuture to the application thread.","commonSituations":"Same root causes as 232 — principal mismatch (ANONYMOUS via misconfigured SASL/JAAS), ACLs granted for a different user or prefix, principal mapping (user:alice vs User:alice) configured differently on the broker, cross-cluster migration without copying ACLs.","solutions":["Grant DESCRIBE (and READ for consume) on the topic to the principal the consumer presents: kafka-acls --add --allow-principal User:<p> --operation Describe --operation Read --topic <t>.","Double-check sasl.mechanism / sasl.jaas.config / security.protocol — a silent ANONYMOUS auth is the most common cause.","Use kafka-acls --list --principal <p> to confirm the ACL is actually attached; if using prefix ACLs verify the topic literal falls under the prefix.","Enable DEBUG org.apache.kafka.clients.consumer.internals.TopicMetadataRequestManager to see the unauthorized topic set."],"exampleFix":"// before\nprops.put(\"sasl.mechanism\", \"PLAIN\");\nprops.put(\"sasl.jaas.config\",\n    \"org.apache.kafka.common.security.plain.PlainLoginModule required username=\\\"alice\\\" password=\\\"***\\\";\");\n// broker expects principal User:alice but ACL was granted to User:bob\n\n// after: align principal with the granted ACL\nbin/kafka-acls.sh --bootstrap-server broker:9092 --add \\\n  --allow-principal User:alice --operation Describe --operation Read --topic orders","handlingStrategy":"try-catch","validationCode":"// Optional ACL preflight via AdminClient before subscribe (heavier than trusting the error path).\ntry (var admin = org.apache.kafka.clients.admin.AdminClient.create(commonProps)) {\n    java.util.Collection<org.apache.kafka.common.acl.AclBinding> have =\n        admin.describeAcls(org.apache.kafka.common.acl.AccessControlEntryFilter.ANY).values().get();\n    // verify required (topic, READ/DESCRIBE) bindings are present\n}","typeGuard":"import org.apache.kafka.common.errors.TopicAuthorizationException;\n\n/** Narrows a throwable to TopicAuthorizationException and returns its unauthorized topic set. */\nstatic java.util.Optional<java.util.Set<String>> unauthorizedTopics(Throwable t) {\n    if (t instanceof TopicAuthorizationException) {\n        return java.util.Optional.of(((TopicAuthorizationException) t).unauthorizedTopics());\n    }\n    return java.util.Optional.empty();\n}","tryCatchPattern":"try {\n    consumer.poll(Duration.ofMillis(1000));\n} catch (org.apache.kafka.common.errors.TopicAuthorizationException e) {\n    java.util.Set<String> unauthorized = e.unauthorizedTopics();\n    log.error(\"Consumer principal not authorized for {}; run kafka-acls to grant READ/DESCRIBE\", unauthorized);\n    // Not self-healing: surface to ops, halt, or fall back to an authorized topic subset.\n    throw e;\n}","preventionTips":["Provision READ + DESCRIBE ACLs for the consumer principal before deploy.","Run an ACL preflight on startup so unauthorized state is caught immediately, not mid-loop.","Verify the principal in your auth config matches the ACL principal string exactly.","Surface unauthorizedTopics() in alerts so ops can remediate without log spelunking."],"tags":["kafka","consumer","async","acl","authorization","metadata","sasl"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}