{"id":"7e1c0fb023bfee3b","repo":"apache/kafka","slug":"unexpected-error-fetching-metadata-for-topic-top-7e1c0f","errorCode":null,"errorMessage":"Unexpected error fetching metadata for topic ${topic}","messagePattern":"Unexpected error fetching metadata for topic (.+?)","errorType":"exception","errorClass":"KafkaException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/clients/consumer/internals/TopicMetadataRequestManager.java","lineNumber":268,"sourceCode":"                // 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;\n                    else if (error.exception() instanceof RetriableException)\n                        throw error.exception();\n                    else\n                        throw new KafkaException(\"Unexpected error fetching metadata for topic \" + topic,\n                            error.exception());\n                }\n            }\n\n            HashMap<String, List<PartitionInfo>> topicsPartitionInfos = new HashMap<>();\n            for (String topic : cluster.topics())\n                topicsPartitionInfos.put(topic, cluster.partitionsForTopic(topic));\n            return topicsPartitionInfos;\n        }\n\n        public String topic() {\n            return topic;\n        }\n    }\n}\n","sourceCodeStart":250,"sourceCodeEnd":284,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/clients/consumer/internals/TopicMetadataRequestManager.java#L250-L284","documentation":"Async-consumer counterpart of error 234. Thrown as a generic KafkaException by TopicMetadataRequestManager.handleTopicMetadataResponse for any per-topic error in the MetadataResponse that is not INVALID_TOPIC_EXCEPTION, UNKNOWN_TOPIC_OR_PARTITION, or a RetriableException. Unlike the legacy path, the async consumer does not loop on retriable errors here — it propagates the retriable exception too, letting the caller decide. The wrapped cause carries the underlying Errors.exception().","triggerScenarios":"MetadataResponse.errors() returns an unexpected error code (e.g. TOPIC_AUTHORIZATION_FAILED when unauthorizedTopics is empty, CLUSTER_AUTHORIZATION_FAILED, NOT_CONTROLLER, or a future error code from a newer broker). Also fires for any RetriableException the async path elects to surface rather than retry.","commonSituations":"Client/broker version skew surfacing an unrecognized code; rare authorization paths; non-standard broker or sidecar that rewrites error codes; transient retriable errors (LEADER_NOT_AVAILABLE) the async layer surfaces instead of masking.","solutions":["Inspect the wrapped cause (exception.getCause()) — the Errors code there is the real signal.","If cause is retriable (RetriableException), wrap the call site in a bounded retry with backoff.","Align client and broker versions to remove unrecognized-code ambiguity.","Enable DEBUG on org.apache.kafka.clients.consumer.internals.TopicMetadataRequestManager — it logs the full error map ('Topic metadata fetch included errors: ...')."],"exampleFix":"// before\nconsumer.partitionsFor(topic).get(); // bubbles up KafkaException with hidden cause\n\n// after\ntry {\n    consumer.partitionsFor(topic).get();\n} catch (ExecutionException e) {\n    Throwable cause = e.getCause() != null ? e.getCause() : e;\n    if (cause instanceof RetriableException) {\n        backoffAndRetry();\n    } else {\n        log.error(\"metadata failed; cause={} {}\", cause.getClass().getSimpleName(), cause.getMessage());\n    }\n}","handlingStrategy":"try-catch","validationCode":"// No general client-side precheck; preflight reachability and version compatibility instead.\ntry (java.net.Socket s = new java.net.Socket()) {\n    s.connect(new java.net.InetSocketAddress(bootstrapHost, bootstrapPort), 2000);\n}","typeGuard":"import org.apache.kafka.common.KafkaException;\n\n/** True iff a throwable is the generic 'Unexpected error fetching metadata' wrapper. */\nstatic boolean isUnexpectedMetadataError(Throwable t) {\n    if (!(t instanceof KafkaException)) return false;\n    String m = t.getMessage();\n    return m != null && m.startsWith(\"Unexpected error fetching metadata for topic\");\n}","tryCatchPattern":"try {\n    consumer.poll(Duration.ofMillis(1000));\n} catch (org.apache.kafka.common.KafkaException e) {\n    if (!isUnexpectedMetadataError(e)) throw e;\n    Throwable cause = e.getCause();\n    log.error(\"Unexpected metadata fetch error; cause={}\", cause, e);\n    if (cause instanceof org.apache.kafka.common.errors.RetriableException) {\n        // backoff and retry\n    } else {\n        throw e;\n    }\n}","preventionTips":["Always inspect getCause() — the real error is nested under the wrapper.","Keep kafka-clients version within the broker's supported client range.","Distinguish retriable causes (LeaderNotSelected, NotEnoughReplicas) from fatal ones.","Alert on the underlying cause class, not the generic wrapper message."],"tags":["kafka","consumer","async","metadata","unexpected","version-skew","diagnostics"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}