apache/pulsar · error · IllegalArgumentException
The value in the record returned by the source cannot be nul
Error message
The value in the record returned by the source cannot be null
What it means
The Source returned a Record whose getValue() is null. readInput eagerly resolves the value so a malformed or poison message fails with full context (message id, topic, key, schema version) via logInputValueDecodeFailure, then rethrows. The function runtime cannot route a null value through the function, so it throws IllegalArgumentException.
Source
Thrown at pulsar-functions/instance/src/main/java/org/apache/pulsar/functions/instance/JavaInstanceRunnable.java:572
if (stats != null) {
stats.incrSourceExceptions(e);
}
log.error().exception(e).log("Encountered exception in source read");
throw e;
} finally {
Thread.currentThread().setContextClassLoader(instanceClassLoader);
}
// check record is valid
if (record == null) {
throw new IllegalArgumentException("The record returned by the source cannot be null");
}
// Eagerly access the value here so a malformed/poison message surfaces with enough
// context (message id, topic, key, schema version) to be located and skipped, instead
// of bubbling up as an opaque crash that names no message.
try {
if (record.getValue() == null) {
throw new IllegalArgumentException("The value in the record returned by the source cannot be null");
}
} catch (Exception e) {
logInputValueDecodeFailure(record, e);
throw e;
}
return record;
}
private void logInputValueDecodeFailure(Record<?> record, Exception e) {
log.warn()
.attr("topic", record.getTopicName().orElse(null))
.attr("messageId", record.getMessage().map(m -> String.valueOf(m.getMessageId())).orElse(null))
.attr("partitionKey", record.getKey().orElse(null))
.attr("schemaVersion", record.getMessage()
.map(Message::getSchemaVersion)
.map(sv -> HexFormat.of().formatHex(sv))
.orElse(null))
.exception(e)View on GitHub (pinned to 820761864e)
Solutions
- Fix the Source or Record implementation to throw a meaningful exception (or skip the record) instead of returning null from getValue()
- Check the logInputValueDecodeFailure log output for topic/message-id/schema-version to locate and delete or repair the poison message
- If using auto schema (AUTO_CONSUME), verify the topic's schema matches the declared function input schema; a mismatch can decode to null
- Validate payloads at the producer side so null/empty values never reach the input topic
Example fix
// before
public String getValue() {
try { return decode(bytes); } catch (Exception e) { return null; }
}
// after
public String getValue() {
try { return decode(bytes); } catch (Exception e) {
throw new RuntimeException("Cannot decode message " + getId().get(), e);
}
} Defensive patterns
Strategy: validation
Validate before calling
Record<String> rec = source.read();
if (rec == null || rec.getValue() == null) {
throw new IllegalStateException("null/null-value record from source");
} Type guard
boolean hasValue(Record<?> r) { return r != null && r.getValue() != null; } Try / catch
try { ctx.newOutputMessage(...).value(rec.getValue()).send(); }
catch (IllegalArgumentException e) { log.error("null record value", e); /* skip */ } Prevention
- Let getValue() throw with message context instead of returning null
- Match the function's declared schema to the topic's actual schema
- Quarantine/delete poison messages identified via topic and message id in logs
- Validate producer payloads upstream to prevent null/empty values
When it happens
Trigger: Record.getValue() deserialization fails and the Record implementation swallows the exception returning null; a Source constructs a Record with a null payload; a schema decode of a malformed message yields null.
Common situations: Poison messages on the input topic (invalid protobuf/avro bytes); custom Record implementations that catch decode exceptions and return null; Sources built over untyped stores where the payload is genuinely missing.
Related errors
- bytes must be >= 0
- Component cannot get state store
- not implemented
- Schema should not be null.
- SourceRecord class type must be PulsarRecord
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/c7688538d9f9b9f0.
Report an issue: GitHub.