apache/kafka · error · IllegalArgumentException
key cannot be null.
Error message
key cannot be null.
What it means
Thrown as IllegalArgumentException by RecordHeaders.checkKey(String) when a null key is passed to lastHeader(key), headers(key), or remove(key). RecordHeaders stores headers keyed by String and uses equals comparisons on keys in iteration, so a null key would both NPE during comparison and is rejected as a programmer error at entry. The guard runs before any iteration, failing fast.
Source
Thrown at clients/src/main/java/org/apache/kafka/common/header/internals/RecordHeaders.java:126
public Iterator<Header> iterator() {
return closeAware(headers.iterator());
}
public void setReadOnly() {
this.isReadOnly = true;
}
public boolean isReadOnly() {
return isReadOnly;
}
public Header[] toArray() {
return headers.isEmpty() ? Record.EMPTY_HEADERS : headers.toArray(new Header[0]);
}
private void checkKey(String key) {
if (key == null)
throw new IllegalArgumentException("key cannot be null.");
}
private void canWrite() {
if (isReadOnly)
throw new IllegalStateException("RecordHeaders has been closed.");
}
private Iterator<Header> closeAware(final Iterator<Header> original) {
return new Iterator<>() {
@Override
public boolean hasNext() {
return original.hasNext();
}
public Header next() {
return original.next();
}
View on GitHub (pinned to c31c9215e1)
Solutions
- Null-check the key before calling lastHeader/headers/remove, and decide explicitly whether to skip the header or throw a domain error.
- Trace the source of the key variable; if it comes from a POJO/mapping, fix the upstream mapping to never yield null for header keys.
- If iterating headers with a filter, prefer iterator() and filter by a non-null constant.
- Add a unit test that asserts the producer path never passes null keys (e.g. via a recording interceptor).
Example fix
// before
String traceId = span.context().traceId(); // may be null
headers.lastHeader(traceId);
// after
String traceId = span.context().traceId();
if (traceId != null) {
headers.lastHeader(traceId);
} Defensive patterns
Strategy: validation
Validate before calling
String key = ...;
if (key == null) {
throw new IllegalArgumentException("header key must not be null");
}
headers.lastHeader(key); // or remove(key) / headers(key) Type guard
static String requireHeaderKey(String key) {
if (key == null) throw new IllegalArgumentException("header key must not be null");
return key;
} Try / catch
try {
headers.remove(key);
} catch (IllegalArgumentException e) {
// message: "key cannot be null."
// caller passed a null header key; fix the upstream source of the key
} Prevention
- Centralize header-key construction so nulls never reach the Headers API
- Never use Optional.orElse(null) to produce a header key
- Reject null keys at the boundary where headers are produced (e.g. your serialization layer)
- Prefer Map entries or records with non-null key types when building headers
When it happens
Trigger: Calling record.headers().lastHeader(null), .headers(null), or .remove(null); passing a header key sourced from a variable that was never initialized or came from a null-producing mapping (e.g. a missing field in an upstream POJO mapped to header keys).
Common situations: Producer interceptor / serializer that converts POJO fields to headers but does not null-check field values; copy-paste of a header key constant that was renamed and now resolves to null; reactive pipeline where a null key slips through from a malformed input record.
Related errors
- Topic partitions to assign to cannot have null or empty topi
- RebalanceListener cannot be null
- Topic pattern to subscribe to cannot be null
- Topic pattern to subscribe to cannot be empty
- Invalid configuration value for 'acks': {acksString}
AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03).
Data as JSON: /data/errors/4c28d960b4621434.json.
Report an issue: GitHub.