{"record":{"id":"6ccfd8253b5fce8c","repo":"apache/pulsar","slug":"invalid-messageidv5-data-bad-parent-topic-length","errorCode":null,"errorMessage":"Invalid MessageIdV5 data: bad parent-topic length","messagePattern":"Invalid MessageIdV5 data: bad parent-topic length","errorType":"exception","errorClass":"java.io.IOException","httpStatus":null,"severity":"error","filePath":"pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/MessageIdV5.java","lineNumber":291,"sourceCode":"            throw new IOException(\"Invalid MessageIdV5 data: bad v4 length\");\n        }\n        byte[] v4Bytes = new byte[v4Length];\n        buf.get(v4Bytes);\n        MessageId v4Id = MessageId.fromByteArray(v4Bytes);\n\n        // Section 3: position vector (single-topic / per-segment).\n        Map<Long, MessageId> positions = Map.of();\n        if (buf.hasRemaining()) {\n            positions = readSegmentVector(buf);\n        }\n\n        // Section 4: parent topic. Length -1 sentinel means \"absent\".\n        String parentTopic = null;\n        if (buf.hasRemaining()) {\n            int parentLen = buf.getInt();\n            if (parentLen >= 0) {\n                if (parentLen > buf.remaining()) {\n                    throw new IOException(\"Invalid MessageIdV5 data: bad parent-topic length\");\n                }\n                byte[] parentBytes = new byte[parentLen];\n                buf.get(parentBytes);\n                parentTopic = new String(parentBytes, StandardCharsets.UTF_8);\n            }\n        }\n\n        // Section 5: cross-topic vector. Count -1 sentinel means \"absent\".\n        Map<String, Map<Long, MessageId>> multiTopic = null;\n        if (buf.hasRemaining()) {\n            int topicCount = buf.getInt();\n            if (topicCount >= 0) {\n                multiTopic = new HashMap<>(topicCount);\n                for (int i = 0; i < topicCount; i++) {\n                    int topicLen = buf.getInt();\n                    byte[] topicBytes = new byte[topicLen];\n                    buf.get(topicBytes);\n                    String topic = new String(topicBytes, StandardCharsets.UTF_8);","sourceCodeStart":273,"sourceCodeEnd":309,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/MessageIdV5.java#L273-L309","documentation":"Section 4 of the MessageIdV5 encoding is an optional parent topic string, prefixed by a 4-byte length where -1 means absent. If a non-negative length exceeds the bytes remaining in the buffer, the data is corrupt or misaligned, so fromByteArray throws IOException('Invalid MessageIdV5 data: bad parent-topic length').","triggerScenarios":"Deserializing a truncated blob where the parent-topic length was read but the string bytes are missing; a length field written in the wrong byte order producing a huge positive value; blobs assembled with a wrong offset skipping the position vector.","commonSituations":"Storage layer truncating trailing bytes; hand-rolled serialization of MessageIdV5 that omits the position-vector section; cross-version formats where the parent-topic section was added later and old readers misparse new blobs.","solutions":["Serialize/deserialize with matching library versions on both sides.","Check the write path flushes the complete blob (verify stored length equals expected).","Validate your buffer offsets: the position vector (section 3) must be fully consumed before the parent-topic section.","Rebuild the id via toByteArray from the original MessageIdV5 object."],"exampleFix":"// before\nByteBuffer buf = ByteBuffer.wrap(data); // data truncated\nMessageIdV5 id = MessageIdV5.fromByteArray(data);\n// after\nif (data.length < expectedMinSize(topicName)) {\n    throw new IOException(\"Stored id truncated (\" + data.length + \" bytes), re-resolve\");\n}\nMessageIdV5 id = MessageIdV5.fromByteArray(data);","handlingStrategy":"validation","validationCode":"// validate overall blob size before parsing sections\nint minLen = 12 + positionVectorSize + 4; // header + vector + parent-topic length field\nif (data.length < minLen) {\n    throw new IOException(\"Blob truncated: expected >= \" + minLen + \", got \" + data.length);\n}","typeGuard":null,"tryCatchPattern":"try {\n    MessageIdV5 id = MessageIdV5.fromByteArray(data);\n} catch (IOException e) {\n    log.warn(\"Truncated/corrupt v5 id, discarding checkpoint\", e);\n}","preventionTips":["Persist the full blob length in a header and verify it after read.","Flush/fsync writes so partial records are never visible to readers.","Round-trip test (toByteArray -> fromByteArray) in CI for any custom persistence layer."],"tags":["deserialization","message-id","corrupt-data"],"backgroundTag":"invalid-message-id-data","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}