{"record":{"id":"e04b03e35f638c30","repo":"pinpoint-apm/pinpoint","slug":"truncated-outserviceuid-tail-remaining-remainin","errorCode":null,"errorMessage":"truncated outServiceUid tail, remaining:${remaining}","messagePattern":"truncated outServiceUid tail, remaining:(.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"commons-server/src/main/java/com/navercorp/pinpoint/common/server/applicationmap/statistics/UidLinkRowKey.java","lineNumber":189,"sourceCode":"        String outApplicationName = buffer.readPrefixedString();\n        String outSubLink = buffer.readPrefixedString();\n\n        // tail-appended link serviceUid; absent in legacy rows -> assume DEFAULT service (no non-DEFAULT legacy data exists)\n        int outServiceUid = getOutServiceUid(buffer);\n\n        return new UidLinkRowKey(serviceUid, applicationName, serviceType,\n                timestamp,\n                outServiceUid, outApplicationName, outServiceType, outSubLink);\n    }\n\n    private static int getOutServiceUid(Buffer buffer) {\n        final int remaining = buffer.remaining();\n        if (remaining == 0) {\n            return ServiceUid.DEFAULT_SERVICE_UID_CODE;\n        }\n        // fail fast on a truncated tail: readInt() does not check the slice bound\n        if (remaining < BytesUtils.INT_BYTE_LENGTH) {\n            throw new IllegalArgumentException(\"truncated outServiceUid tail, remaining:\" + remaining);\n        }\n        return buffer.readInt();\n    }\n\n\n    @Override\n    public boolean equals(Object o) {\n        if (o == null || getClass() != o.getClass()) return false;\n\n        UidLinkRowKey that = (UidLinkRowKey) o;\n        return serviceUid == that.serviceUid && serviceType == that.serviceType && timestamp == that.timestamp && linkServiceType == that.linkServiceType && linkServiceUid == that.linkServiceUid && Objects.equals(applicationName, that.applicationName) && Objects.equals(linkApplicationName, that.linkApplicationName) && Objects.equals(subLink, that.subLink);\n    }\n\n    @Override\n    public int hashCode() {\n        int result = serviceUid;\n        result = 31 * result + Objects.hashCode(applicationName);\n        result = 31 * result + serviceType;","sourceCodeStart":171,"sourceCodeEnd":207,"githubUrl":"https://github.com/pinpoint-apm/pinpoint/blob/744c3d3075e595656abb1ae331ad2c0e4c9eb996/commons-server/src/main/java/com/navercorp/pinpoint/common/server/applicationmap/statistics/UidLinkRowKey.java#L171-L207","documentation":"UidLinkRowKey.getOutServiceUid deserializes the trailing outServiceUid int from the row-key ByteBuffer. A remaining slice of 1-3 bytes means the buffer is truncated (readInt needs exactly 4 bytes and does not itself bound-check), so an IllegalArgumentException is thrown to fail fast instead of reading garbage or underflowing.","triggerScenarios":"Reading a UidLink row key whose serialized bytes are shorter than expected: a key persisted by a schema/version without the outServiceUid tail, or a byte array that was cut off during storage/transfer, with 1-3 bytes left at the tail.","commonSituations":"Version mismatch between writer and reader of LinkRowKeys (schema evolved to append outServiceUid); data migrated from older tables; hand-built byte arrays passed to the row key parser; corrupted HBase cells.","solutions":["Check that the writer and reader use the same UidLink row-key schema/version (keys that include outServiceUid)","Verify the stored byte array length matches the expected key size before constructing the row key","Regenerate or repair affected rows; treat 1-3-byte-tail keys as legacy and fall back to the default service uid path","Guard with buffer.remaining() >= BytesUtils.INT_BYTE_LENGTH before attempting to read the tail"],"exampleFix":"// before\nUidLinkRowKey key = UidLinkRowKey.read(bytes);\n// after\nif (bytes == null || (bytes.length % keyUnit) != 0 || bytes.length < minKeyLength) {\n    throw new IllegalArgumentException(\"unexpected UidLink key length: \" + (bytes == null ? -1 : bytes.length));\n}\nUidLinkRowKey key = UidLinkRowKey.read(bytes);","handlingStrategy":"try-catch","validationCode":"boolean isWellFormedUidLinkKey(byte[] rowKey, int expectedTailBytes) {\n    return rowKey != null && rowKey.length >= expectedTailBytes;\n}","typeGuard":"boolean isWellFormedUidLinkKey(byte[] rowKey, int expectedTailBytes) {\n    return rowKey != null && rowKey.length >= expectedTailBytes;\n}","tryCatchPattern":"try {\n    long outServiceUid = rowKey.getOutServiceUid();\n} catch (IllegalArgumentException e) {\n    log.warn(\"truncated UidLink key, treating as legacy: {}\", e.getMessage());\n    long outServiceUid = ServiceUid.DEFAULT_SERVICE_UID_CODE; // legacy fallback\n}","preventionTips":["Keep writer and reader row-key schema versions in sync","Validate byte-array lengths against the expected key layout before parsing","Handle legacy keys (without outServiceUid tail) with an explicit code path","Detect storage corruption/migration issues early with key-length assertions"],"tags":["hbase","deserialization","corrupt-data","rowkey"],"backgroundTag":"schema-validation-failed","analyzedSha":"744c3d3075e595656abb1ae331ad2c0e4c9eb996","analyzedAt":"2026-09-07T18:48:45.289Z","contentChangedAt":"2026-09-07T18:48:45.289Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}