apache/kafka · error · InvalidTopicException
Topic '${topic}' is invalid
Error message
Topic '${topic}' is invalid What it means
Thrown as InvalidTopicException by TopicMetadataFetcher.getTopicMetadata when the broker's MetadataResponse carries Errors.INVALID_TOPIC_EXCEPTION for at least one requested topic. INVALID_TOPIC_EXCEPTION on the broker side means the topic name failed server-side validation (illegal characters, length, or '.' / '_' conflicts), distinct from UNKNOWN_TOPIC_OR_PARTITION which is silently skipped. The client surfaces it as a hard, non-retriable failure.
Source
Thrown at clients/src/main/java/org/apache/kafka/clients/consumer/internals/TopicMetadataFetcher.java:128
Set<String> unauthorizedTopics = cluster.unauthorizedTopics();
if (!unauthorizedTopics.isEmpty())
throw new TopicAuthorizationException(unauthorizedTopics);
boolean shouldRetry = false;
Map<String, Errors> errors = response.errors();
if (!errors.isEmpty()) {
// if there were errors, we need to check whether they were fatal or whether
// we should just retry
log.debug("Topic metadata fetch included errors: {}", errors);
for (Map.Entry<String, Errors> errorEntry : errors.entrySet()) {
String topic = errorEntry.getKey();
Errors error = errorEntry.getValue();
if (error == Errors.INVALID_TOPIC_EXCEPTION)
throw new InvalidTopicException("Topic '" + topic + "' is invalid");
else if (error == Errors.UNKNOWN_TOPIC_OR_PARTITION)
// if a requested topic is unknown, we just continue and let it be absent
// in the returned map
continue;
else if (error.exception() instanceof RetriableException)
shouldRetry = true;
else
throw new KafkaException("Unexpected error fetching metadata for topic " + topic,
error.exception());
}
}
if (!shouldRetry) {
HashMap<String, List<PartitionInfo>> topicsPartitionInfos = new HashMap<>();
for (String topic : cluster.topics())
topicsPartitionInfos.put(topic, cluster.partitionsForTopic(topic));
return topicsPartitionInfos;
}View on GitHub (pinned to c31c9215e1)
Solutions
- Sanitize the topic name to match ^[a-zA-Z0-9\._-]{1,249}$ and avoid '.' or '..' exactly.
- If the topic was meant to exist, verify it via kafka-topics --describe; recreate with a valid name if it was auto-created with a bad name.
- Patch the producer of the bad topic name (config templating, env-var interpolation) so future runs don't recreate it.
- Disable auto.create.topics.enable on the broker if stray names are getting auto-created.
Example fix
// before
consumer.partitionsFor("orders/eur"); // '/' is illegal
// after
String topic = "orders.eur"; // legal charset
consumer.partitionsFor(topic); Defensive patterns
Strategy: validation
Validate before calling
// Validate topic name against Kafka naming rules before subscribe/partitionFor.
static final java.util.regex.Pattern NAME = java.util.regex.Pattern.compile("^[a-zA-Z0-9._-]+$");
static boolean isValidTopicName(String t) {
return t != null && t.length() >= 1 && t.length() <= 249
&& !t.equals(".") && !t.equals("..")
&& NAME.matcher(t).matches();
}
if (!isValidTopicName(topic)) throw new IllegalArgumentException("bad topic name: " + topic); Type guard
import java.util.regex.Pattern;
/** Narrows a String to a Kafka-valid topic name (length 1-249, [A-Za-z0-9._-], not '.' / '..'). */
static boolean isValidTopicName(String topic) {
if (topic == null || topic.isEmpty() || topic.length() > 249) return false;
if (topic.equals(".") || topic.equals("..")) return false;
for (int i = 0; i < topic.length(); i++) {
char c = topic.charAt(i);
boolean ok = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
|| (c >= '0' && c <= '9') || c == '.' || c == '_' || c == '-';
if (!ok) return false;
}
return true;
} Try / catch
try {
consumer.partitionsFor(topic);
} catch (org.apache.kafka.common.errors.InvalidTopicException e) {
log.warn("Rejected invalid topic name '{}'; fix config/source, do not retry as-is", topic);
throw e; // not retriable — the name will not become valid on its own
} Prevention
- Validate topic names at config/source ingestion time, not at the Kafka call site.
- Wrap topic creation in a factory that enforces the naming regex and length cap (249).
- Reject '.' and '..' and names with '/' or control characters explicitly.
- Add a unit test over your topic-name source so invalid names never reach the client.
When it happens
Trigger: Calling consumer.partitionsFor(topic) / listTopics() / subscribe() with a topic name containing illegal characters (must match [a-zA-Z0-9._-], max 249 chars), or a name equal to '.' or '..'. Also triggered when a topic name conflicts with an internal one.
Common situations: Topic names with forward slash '/', comma, or colon from templated config; names longer than 249 chars from dynamic naming; topic auto-created by another client with a typo (e.g. 'orders/eur'); using a fully-qualified 'namespace.topic' style where the namespace has illegal characters.
Related errors
- Topic '${topic}' is invalid
- The target time for partition {} is {}. The target time cann
- The timeout cannot be negative.
- Topic partitions collection to assign to cannot be null
- Topic collection to subscribe to cannot be null
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/b79ff38411bd7e47.json.
Report an issue: GitHub.