{"id":"2d0fad9e4fc708bb","repo":"apache/kafka","slug":"invalid-message-timestamp-2d0fad","errorCode":null,"errorMessage":"Invalid message timestamp {}","messagePattern":"Invalid message timestamp (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/record/internal/LegacyRecord.java","lineNumber":449,"sourceCode":"                             long timestamp,\n                             byte[] key,\n                             byte[] value) throws IOException {\n        write(out, magic, crc, attributes, timestamp, wrapNullable(key), wrapNullable(value));\n    }\n\n    // Write a record to the buffer, if the record's compression type is none, then\n    // its value payload should be already compressed with the specified type\n    private static void write(DataOutputStream out,\n                              byte magic,\n                              long crc,\n                              byte attributes,\n                              long timestamp,\n                              ByteBuffer key,\n                              ByteBuffer value) throws IOException {\n        if (magic != RecordBatch.MAGIC_VALUE_V0 && magic != RecordBatch.MAGIC_VALUE_V1)\n            throw new IllegalArgumentException(\"Invalid magic value \" + magic);\n        if (timestamp < 0 && timestamp != RecordBatch.NO_TIMESTAMP)\n            throw new IllegalArgumentException(\"Invalid message timestamp \" + timestamp);\n\n        // write crc\n        out.writeInt((int) (crc & 0xffffffffL));\n        // write magic value\n        out.writeByte(magic);\n        // write attributes\n        out.writeByte(attributes);\n\n        // maybe write timestamp\n        if (magic > RecordBatch.MAGIC_VALUE_V0)\n            out.writeLong(timestamp);\n\n        // write the key\n        if (key == null) {\n            out.writeInt(-1);\n        } else {\n            int size = key.remaining();\n            out.writeInt(size);","sourceCodeStart":431,"sourceCodeEnd":467,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/record/internal/LegacyRecord.java#L431-L467","documentation":"Thrown by LegacyRecord.write() when serializing a legacy (v0/v1) message: a record timestamp must be either non-negative or exactly RecordBatch.NO_TIMESTAMP (the sentinel -1 that means 'no timestamp'). Any other negative value is rejected because the on-wire timestamp field is an unsigned 8-byte value and negative numbers are not representable/valid. The guard protects against writing corrupt frames that would later fail CRC or parsing.","triggerScenarios":"Calling any of the LegacyRecord.write(...) overloads (or the higher-level MemoryRecordsBuilder path that targets magic=0/1) with a timestamp such as -2, Long.MIN_VALUE, or any negative value other than -1. Most commonly reached by tests or custom serializers that hand-craft records, or by mis-mapping a 'missing timestamp' sentinel to something other than NO_TIMESTAMP before writing.","commonSituations":"Reusing a sentinel constant (e.g. -1 from a different schema, or 0 interpreted as 'unset' then decremented), off-by-one timestamp math, arithmetic underflow when subtracting offsets, or a producer/serializer library that represents 'no timestamp' as a different negative value than Kafka's NO_TIMESTAMP.","solutions":["Set the timestamp to RecordBatch.NO_TIMESTAMP (-1L) when you mean 'no timestamp', not any other negative number.","Ensure any timestamp arithmetic cannot produce a negative result; clamp to >=0 or NO_TIMESTAMP.","If forwarding timestamps from an upstream schema, map its null/missing value to RecordBatch.NO_TIMESTAMP before calling LegacyRecord.write."],"exampleFix":"// before\nLegacyRecord.write(out, magic, crc, attributes, userTimestamp - offset, key, value);\n// after\nlong ts = userTimestamp == null ? RecordBatch.NO_TIMESTAMP : Math.max(0, userTimestamp - offset);\nLegacyRecord.write(out, magic, crc, attributes, ts, key, value);","handlingStrategy":"validation","validationCode":"// Validate timestamp before serializing a LegacyRecord (magic v1).\n// RecordBatch.NO_TIMESTAMP (-1) is the only allowed non-positive value.\nlong NO_TIMESTAMP = -1L;\nif (timestamp < 0L && timestamp != NO_TIMESTAMP) {\n    throw new IllegalArgumentException(\n        \"Rejecting record: timestamp \" + timestamp + \" must be >= 0 or NO_TIMESTAMP(-1)\");\n}","typeGuard":"// Timestamp must be a non-negative long, or the sentinel NO_TIMESTAMP(-1).\nstatic boolean isValidLegacyTimestamp(long ts) {\n    return ts >= 0L || ts == -1L;\n}","tryCatchPattern":"try {\n    // ... call API that serializes a LegacyRecord (e.g. MemoryRecordsBuilder.append) ...\n} catch (IllegalArgumentException e) {\n    if (e.getMessage() != null && e.getMessage().startsWith(\"Invalid message timestamp\")) {\n        // Drop / route-to-DLT the offending record; never silently retry with same ts.\n        throw new RecordSerializationException(\"Bad producer timestamp\", e);\n    }\n    throw e;\n}","preventionTips":["Always set producer record timestamps via System.currentTimeMillis() or let the producer default to CREATE_TIME instead of injecting raw/external values.","Treat 0 as the floor for real timestamps; reserve -1 (RecordBatch.NO_TIMESTAMP) only when you genuinely mean 'no timestamp'.","If ingesting timestamps from an upstream source (DB row, external event), sanitize/clamp them in a Serializer or ProducerInterceptor before send().","Add a unit test asserting that negative non-sentinel timestamps are rejected before reaching the wire."],"tags":["kafka","records","serialization","validation","legacy"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}