{"id":"e9b5b80ef3bc82a2","repo":"apache/kafka","slug":"invalid-magic-used-in-legacyrecord","errorCode":null,"errorMessage":"Invalid magic used in LegacyRecord: {}","messagePattern":"Invalid magic used in LegacyRecord: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"clients/src/main/java/org/apache/kafka/common/record/internal/LegacyRecord.java","lineNumber":547,"sourceCode":"            Checksums.update(crc, key, size);\n        }\n        // update for the value\n        if (value == null) {\n            Checksums.updateInt(crc, -1);\n        } else {\n            int size = value.remaining();\n            Checksums.updateInt(crc, size);\n            Checksums.update(crc, value, size);\n        }\n        return crc.getValue();\n    }\n\n    static int recordOverhead(byte magic) {\n        if (magic == 0)\n            return RECORD_OVERHEAD_V0;\n        else if (magic == 1)\n            return RECORD_OVERHEAD_V1;\n        throw new IllegalArgumentException(\"Invalid magic used in LegacyRecord: \" + magic);\n    }\n\n    static int headerSize(byte magic) {\n        if (magic == 0)\n            return HEADER_SIZE_V0;\n        else if (magic == 1)\n            return HEADER_SIZE_V1;\n        throw new IllegalArgumentException(\"Invalid magic used in LegacyRecord: \" + magic);\n    }\n\n    private static int keyOffset(byte magic) {\n        if (magic == 0)\n            return KEY_OFFSET_V0;\n        else if (magic == 1)\n            return KEY_OFFSET_V1;\n        throw new IllegalArgumentException(\"Invalid magic used in LegacyRecord: \" + magic);\n    }\n","sourceCodeStart":529,"sourceCodeEnd":565,"githubUrl":"https://github.com/apache/kafka/blob/c31c9215e131f8c17e79f8901b48c13ee6aa8e7a/clients/src/main/java/org/apache/kafka/common/record/internal/LegacyRecord.java#L529-L565","documentation":"Thrown by LegacyRecord.recordOverhead() (and the parallel headerSize() at line 555) when computing the per-record overhead/header size for a magic value that is neither 0 (V0) nor 1 (V1). LegacyRecord only supports the two historical message formats, so any other byte (e.g. 2, the v2 default) is meaningless here and indicates the caller used the wrong record class.","triggerScenarios":"Calling recordOverhead(magic) or headerSize(magic) with magic != 0 and magic != 1. Most often hit when a code path assumes the modern default magic value (2, RecordBatch.MAGIC_VALUE_V2) is passed through LegacyRecord instead of the newer DefaultRecord/DefaultRecordBatch APIs.","commonSituations":"Refactoring a record path from legacy to v2 and forgetting to switch from LegacyRecord to DefaultRecordBatch; defaulting magic to the current value (2) and feeding it into a legacy-only utility; reading a v2 batch through a legacy decoder.","solutions":["Route magic==2 (and above) through DefaultRecord / DefaultRecordBatch, not LegacyRecord.","Validate magic against RecordBatch.MAGIC_VALUE_V0 / MAGIC_VALUE_V1 before calling recordOverhead/headerSize.","Ensure any 'default magic' constant used in the call site is the legacy value when LegacyRecord is intended."],"exampleFix":"// before\nint overhead = LegacyRecord.recordOverhead(batchMagic); // batchMagic == 2\n// after\nif (batchMagic < RecordBatch.MAGIC_VALUE_V2) {\n    int overhead = LegacyRecord.recordOverhead(batchMagic);\n} else {\n    int overhead = DefaultRecord.recordOverhead(...);\n}","handlingStrategy":"type-guard","validationCode":"// LegacyRecord only supports magic 0 (v0) and 1 (v1).\nif (magic != RecordBatch.MAGIC_VALUE_V0 && magic != RecordBatch.MAGIC_VALUE_V1) {\n    throw new IllegalArgumentException(\n        \"Unsupported legacy magic \" + magic + \"; expected 0 or 1\");\n}","typeGuard":"// Accept only the two legacy magic values before any LegacyRecord call.\nstatic boolean isLegacyMagic(byte magic) {\n    return magic == RecordBatch.MAGIC_VALUE_V0 || magic == RecordBatch.MAGIC_VALUE_V1;\n}","tryCatchPattern":"try {\n    int overhead = LegacyRecord.recordOverhead(magic);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().startsWith(\"Invalid magic used in LegacyRecord\")) {\n        // Upgrade to the v2 record batch path; legacy format cannot represent this magic.\n        magic = RecordBatch.MAGIC_VALUE_V2;\n    } else {\n        throw e;\n    }\n}","preventionTips":["Do not invent custom magic values; only 0, 1 (legacy) and 2 (current v2 batch) are valid across the codebase.","When down-converting or reading old log segments, validate magic up front and route unknown values to an upgrade/migration path rather than passing them through.","Pin the broker's inter.broker.protocol.version / message.format.version so magic values stay within the supported set for your cluster version."],"tags":["kafka","records","versioning","legacy","validation"],"analyzedSha":"c31c9215e131f8c17e79f8901b48c13ee6aa8e7a","analyzedAt":"2026-08-03T12:34:05.770Z","schemaVersion":2}