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 v1

What it means

Thrown by LegacyRecord.computeAttributes() when computing the attributes byte for message format v1 (magic > MAGIC_VALUE_V0). For v1 records the timestamp type is a load-bearing attribute bit, so the caller must explicitly say whether the timestamp is CreateTime or LogAppendTime. Passing TimestampType.NO_TIMESTAMP_TYPE means the caller forgot to choose, which would silently encode an incorrect attributes byte.

Source

Thrown at clients/src/main/java/org/apache/kafka/common/record/internal/LegacyRecord.java:495

        }
    }

    static int recordSize(byte magic, ByteBuffer key, ByteBuffer value) {
        return recordSize(magic, key == null ? 0 : key.limit(), value == null ? 0 : value.limit());
    }

    public static int recordSize(byte magic, int keySize, int valueSize) {
        return recordOverhead(magic) + keySize + valueSize;
    }

    // visible only for testing
    public static byte computeAttributes(byte magic, CompressionType type, TimestampType timestampType) {
        byte attributes = 0;
        if (type.id > 0)
            attributes |= (byte) (COMPRESSION_CODEC_MASK & type.id);
        if (magic > RecordBatch.MAGIC_VALUE_V0) {
            if (timestampType == TimestampType.NO_TIMESTAMP_TYPE)
                throw new IllegalArgumentException("Timestamp type must be provided to compute attributes for " +
                        "message format v1");
            if (timestampType == TimestampType.LOG_APPEND_TIME)
                attributes |= TIMESTAMP_TYPE_MASK;
        }
        return attributes;
    }

    // visible only for testing
    public static long computeChecksum(byte magic, byte attributes, long timestamp, byte[] key, byte[] value) {
        return computeChecksum(magic, attributes, timestamp, wrapNullable(key), wrapNullable(value));
    }

    private static long crc32(ByteBuffer buffer, int offset, int size) {
        CRC32 crc = new CRC32();
        Checksums.update(crc, buffer, offset, size);
        return crc.getValue();
    }

View on GitHub (pinned to c31c9215e1)

Solutions

  1. Pass TimestampType.CREATE_TIME or TimestampType.LOG_APPEND_TIME explicitly when magic is v1.
  2. If constructing via MemoryRecordsBuilder, supply the TimestampType argument to the builder rather than relying on a default.
  3. For v0 records keep magic=MAGIC_VALUE_V0 so the timestamp-type bit is not required.

Example fix

// before
byte attrs = LegacyRecord.computeAttributes(RecordBatch.MAGIC_VALUE_V1, CompressionType.NONE, TimestampType.NO_TIMESTAMP_TYPE);
// after
byte attrs = LegacyRecord.computeAttributes(RecordBatch.MAGIC_VALUE_V1, CompressionType.NONE, TimestampType.CREATE_TIME);
Defensive patterns

Strategy: validation

Validate before calling

// computeAttributes requires a concrete TimestampType when magic == V1.
byte magic = RecordBatch.MAGIC_VALUE_V1;
if (magic > RecordBatch.MAGIC_VALUE_V0
        && (timestampType == null || timestampType == TimestampType.NO_TIMESTAMP_TYPE)) {
    throw new IllegalArgumentException(
        "TimestampType.CREATE_TIME or LOG_APPEND_TIME is required for magic v1");
}

Type guard

// Narrow TimestampType to one of the two legal v1 values.
static boolean isValidV1TimestampType(TimestampType t) {
    return t == TimestampType.CREATE_TIME || t == TimestampType.LOG_APPEND_TIME;
}

Try / catch

try {
    byte attrs = LegacyRecord.computeAttributes(magic, compression, timestampType);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Timestamp type must be provided")) {
        // Default to CREATE_TIME for v1 records rather than failing the produce path.
        timestampType = TimestampType.CREATE_TIME;
        attrs = LegacyRecord.computeAttributes(magic, compression, timestampType);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Calling computeAttributes(magic=RecordBatch.MAGIC_VALUE_V1, type, TimestampType.NO_TIMESTAMP_TYPE). Reached indirectly through MemoryRecordsBuilder when it is constructed for v1 without a timestamp type, or via tests/benchmarks that pass the default NO_TIMESTAMP_TYPE.

Common situations: Building a v1 MemoryRecordsBuilder without specifying TimestampType, or copying code that worked for v0 (which has no timestamp-type bit) into a v1 path. Config mistakes where a producer or test harness uses LOG_APPEND_TIME/CREATE_TIME constants inconsistently.

Related errors


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