apache/kafka · error · InvalidTopicException
Topic name is invalid: {reasonInvalid}
Error message
Topic name is invalid: {reasonInvalid} What it means
Thrown by Topic.validate(topic) when a topic name fails Kafka's naming rules, surfaced as an InvalidTopicException. The validation rejects empty strings, "." and "..", names longer than 249 characters, and any character outside ASCII alphanumerics plus '.', '_', '-'. The library enforces this on essentially every API that creates, produces to, or assigns a topic so that illegal names fail fast at the client rather than at the broker.
Source
Thrown at clients/src/main/java/org/apache/kafka/common/internals/Topic.java:43
public class Topic {
public static final String GROUP_METADATA_TOPIC_NAME = "__consumer_offsets";
public static final String TRANSACTION_STATE_TOPIC_NAME = "__transaction_state";
public static final String SHARE_GROUP_STATE_TOPIC_NAME = "__share_group_state";
public static final String CLUSTER_METADATA_TOPIC_NAME = "__cluster_metadata";
public static final TopicPartition CLUSTER_METADATA_TOPIC_PARTITION = new TopicPartition(
CLUSTER_METADATA_TOPIC_NAME,
0
);
public static final String LEGAL_CHARS = "[a-zA-Z0-9._-]";
private static final Set<String> INTERNAL_TOPICS = Set.of(GROUP_METADATA_TOPIC_NAME, TRANSACTION_STATE_TOPIC_NAME, SHARE_GROUP_STATE_TOPIC_NAME);
private static final int MAX_NAME_LENGTH = 249;
public static void validate(String topic) {
validate(topic, "Topic name", message -> {
throw new InvalidTopicException(message);
});
}
private static String detectInvalidTopic(String name) {
if (name.isEmpty())
return "the empty string is not allowed";
if (".".equals(name))
return "'.' is not allowed";
if ("..".equals(name))
return "'..' is not allowed";
if (name.length() > MAX_NAME_LENGTH)
return "the length of '" + name + "' is longer than the max allowed length " + MAX_NAME_LENGTH;
if (!containsValidPattern(name))
return "'" + name + "' contains one or more characters other than " +
"ASCII alphanumerics, '.', '_' and '-'";
return null;
}
View on GitHub (pinned to c31c9215e1)
Solutions
- Sanitize the topic name before use: trim, replace any character outside [a-zA-Z0-9._-] with '_' or '-', and collapse empty/null to a known default.
- Enforce the 249-character max length in your topic-naming layer and fail with a clear application error before the value reaches the Kafka client.
- Reject null/blank topic inputs at your config or request boundary (Objects.requireNonNull / StringUtils.isBlank) so the real source of the missing value is obvious.
- If a tenant or schema identifier legitimately contains '/', '.', or ':', map it through a deterministic encoding scheme before forming the Kafka topic name.
- Re-read the message suffix — it states the exact reason ('the empty string is not allowed', 'contains one or more characters other than...', length violation) which points directly at which rule was broken.
Example fix
// before — untrusted value used directly
String topic = request.getPath(); // e.g. "orders/eu-west-1"
producer.send(new ProducerRecord<>(topic, key, value)); // throws InvalidTopicException
// after — normalize at the boundary
static String safeTopic(String raw) {
if (raw == null || raw.isBlank()) throw new IllegalArgumentException("topic required");
String t = raw.trim().replaceAll("[^a-zA-Z0-9._-]", "_");
if (t.length() > 249) throw new IllegalArgumentException("topic too long: " + raw);
return t;
}
producer.send(new ProducerRecord<>(safeTopic(request.getPath()), key, value)); Defensive patterns
Strategy: validation
Validate before calling
// Validate before using the topic name in any API call.
import org.apache.kafka.common.internals.Topic;
import org.apache.kafka.common.errors.InvalidTopicException;
String topic = ...;
if (!Topic.isValid(topic)) {
// reject at the boundary; never pass to producer/consumer/admin.
throw new IllegalArgumentException("Refusing to use invalid topic: " + topic);
}
// Use org.apache.kafka.common.internals.Topic.validate(topic) if you prefer InvalidTopicException. Type guard
// Narrow user-supplied strings to 'valid Kafka topic name'.
static String requireValidTopic(String name) {
if (name == null || !Topic.isValid(name)) {
throw new IllegalArgumentException("Invalid Kafka topic name: " + name);
}
return name;
} Try / catch
try {
Topic.validate(topic);
} catch (InvalidTopicException e) {
// turn a validation failure into a user-facing 400 / reject the request.
handleBadRequest(e.getMessage());
} Prevention
- Topic names must match [a-zA-Z0-9._-], be non-empty, not equal '.' or '..', and be at most 249 characters — validate at every input boundary.
- Prefer building topic names from an allow-list of known identifiers rather than free-form user input.
- Avoid both '.' and '_' in topic names when possible; they collide in JMX metric names (see Topic.hasCollisionChars).
- Validate names once at ingestion, not on every produce/consume call, to keep hot paths allocation-free.
When it happens
Trigger: Calling KafkaProducer.send() with a ProducerRecord whose topic name is empty, contains a '/' or space, exceeds 249 chars, or is literally "." / "..". Also triggered by AdminClient.createTopics(), consumer.subscribe(), KafkaStreams builder.topic(), and assignment APIs — all of which delegate to Topic.validate(). The error message is the logPrefix ("Topic name") followed by the specific reason from detectInvalidTopic().
Common situations: Topic name built from untrusted input (URL path, table name, tenant id) containing '/', ':', '@', spaces, or Unicode; null/empty string passed because a config lookup or env var was missing; topic name truncated to "." or ".." by a buggy string transform; very long dynamically-generated names (e.g. '<tenant>.<schema>.<table>.<stream>') exceeding 249 chars; misconfigured connect converter reading topic from a record field that is null.
Related errors
- requested size {sizeBytes}<=0
- Invalid negative offset
- Invalid negative offset
- Invalid negative timestamp
- Invalid value `{}` for configuration {}. The value must be e
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/4c4372b9bc66b974.json.
Report an issue: GitHub.