apache/kafka · error · IllegalArgumentException

Timestamp type must be provided to compute attributes for me

Error message

Timestamp type must be provided to compute attributes for message format v2 and above

What it means

Thrown by DefaultRecordBatch.computeAttributes when timestampType equals TimestampType.NO_TIMESTAMP_TYPE. The v2 batch attributes byte encodes, among other things, whether timestamps are CREATE_TIME or LOG_APPEND_TIME; a writer must decide one before serializing, so 'no type' is rejected. IllegalArgumentException from the producer/broker write path.

Source

Thrown at clients/src/main/java/org/apache/kafka/common/record/internal/DefaultRecordBatch.java:427

    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        DefaultRecordBatch that = (DefaultRecordBatch) o;
        return Objects.equals(buffer, that.buffer);
    }

    @Override
    public int hashCode() {
        return buffer != null ? buffer.hashCode() : 0;
    }

    private static byte computeAttributes(CompressionType type, TimestampType timestampType,
                                          boolean isTransactional, boolean isControl, boolean isDeleteHorizonSet) {
        if (timestampType == TimestampType.NO_TIMESTAMP_TYPE)
            throw new IllegalArgumentException("Timestamp type must be provided to compute attributes for message " +
                    "format v2 and above");

        byte attributes = isTransactional ? TRANSACTIONAL_FLAG_MASK : 0;
        if (isControl)
            attributes |= CONTROL_FLAG_MASK;
        if (type.id > 0)
            attributes |= (byte) (COMPRESSION_CODEC_MASK & type.id);
        if (timestampType == TimestampType.LOG_APPEND_TIME)
            attributes |= TIMESTAMP_TYPE_MASK;
        if (isDeleteHorizonSet)
            attributes |= DELETE_HORIZON_FLAG_MASK;
        return attributes;
    }

    public static void writeEmptyHeader(ByteBuffer buffer,
                                        byte magic,
                                        long producerId,
                                        short producerEpoch,

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Pass an explicit TimestampType.CREATE_TIME or TimestampType.LOG_APPEND_TIME to the builder/writeHeader call.
  2. Prefer the high-level MemoryRecords.builder(...) factories, which default to a valid TimestampType, over writeHeader directly.
  3. If the choice should depend on broker config (message.timestamp.type), read that config and forward the resolved TimestampType rather than NO_TIMESTAMP_TYPE.

Example fix

// before
writeHeader(buf, baseOffset, delta, size, magic,
    compression, TimestampType.NO_TIMESTAMP_TYPE, ...);

// after
TimestampType type = afterLogAppend ? TimestampType.LOG_APPEND_TIME
                                    : TimestampType.CREATE_TIME;
writeHeader(buf, baseOffset, delta, size, magic, compression, type, ...);
Defensive patterns

Strategy: validation

Validate before calling

// This is an internal writer path (computeAttributes), reached only when constructing v2+
// batches. End users normally don't call it directly, but custom MemoryRecordsBuilder users
// must pass a non-NO_TIMESTAMP_TYPE:
TimestampType requireTimestampType(TimestampType t) {
    if (t == null || t == TimestampType.NO_TIMESTAMP_TYPE)
        throw new IllegalArgumentException("TimestampType required for v2 batches; use CREATE_TIME or LOG_APPEND_TIME");
    return t;
}

Type guard

// Narrow to a non-null, meaningful type at API boundaries:
static final class ResolvedTimestampType {
    private final TimestampType t;
    private ResolvedTimestampType(TimestampType t) { this.t = t; }
    static ResolvedTimestampType of(TimestampType in) {
        if (in == TimestampType.NO_TIMESTAMP_TYPE)
            throw new IllegalArgumentException("resolve to CREATE_TIME/LOG_APPEND_TIME first");
        return new ResolvedTimestampType(in);
    }
    TimestampType get() { return t; }
}

Try / catch

// Defensive guard if you build batches with pluggable timestamp policies:
try {
    writeHeader(buf, ..., timestampType, ...);
} catch (IllegalArgumentException e) { // missing timestamp type
    log.warn("Timestamp policy returned NO_TIMESTAMP_TYPE; defaulting to CREATE_TIME", e);
    writeHeader(buf, ..., TimestampType.CREATE_TIME, ...);
}

Prevention

When it happens

Trigger: Produced when writeHeader/computeAttributes is invoked with TimestampType.NO_TIMESTAMP_TYPE — typically a test helper or custom MemoryRecordsBuilder invocation that did not pin a timestamp type. Broker-side, the append path always sets LOG_APPEND_TIME or carries the producer's CREATE_TIME, so hitting this from production code implies a misconfigured builder.

Common situations: Unit tests that hand-roll batches via DefaultRecordBatch.writeHeader and pass the default TimestampType; custom tools that build control batches or tombstones without setting timestampType; integrations that conditionally omit the timestamp type when 'not appending time'.

Related errors


AI-assisted analysis of apache/kafka@c31c9215e1 (2026-08-03). Data as JSON: /data/errors/8f2216283e6b84ac.json. Report an issue: GitHub.