pinpoint-apm/pinpoint · error · IllegalArgumentException

truncated outServiceUid tail, remaining:${remaining}

Error message

truncated outServiceUid tail, remaining:${remaining}

What it means

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.

Source

Thrown at commons-server/src/main/java/com/navercorp/pinpoint/common/server/applicationmap/statistics/UidLinkRowKey.java:189

        String outApplicationName = buffer.readPrefixedString();
        String outSubLink = buffer.readPrefixedString();

        // tail-appended link serviceUid; absent in legacy rows -> assume DEFAULT service (no non-DEFAULT legacy data exists)
        int outServiceUid = getOutServiceUid(buffer);

        return new UidLinkRowKey(serviceUid, applicationName, serviceType,
                timestamp,
                outServiceUid, outApplicationName, outServiceType, outSubLink);
    }

    private static int getOutServiceUid(Buffer buffer) {
        final int remaining = buffer.remaining();
        if (remaining == 0) {
            return ServiceUid.DEFAULT_SERVICE_UID_CODE;
        }
        // fail fast on a truncated tail: readInt() does not check the slice bound
        if (remaining < BytesUtils.INT_BYTE_LENGTH) {
            throw new IllegalArgumentException("truncated outServiceUid tail, remaining:" + remaining);
        }
        return buffer.readInt();
    }


    @Override
    public boolean equals(Object o) {
        if (o == null || getClass() != o.getClass()) return false;

        UidLinkRowKey that = (UidLinkRowKey) o;
        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);
    }

    @Override
    public int hashCode() {
        int result = serviceUid;
        result = 31 * result + Objects.hashCode(applicationName);
        result = 31 * result + serviceType;

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Check that the writer and reader use the same UidLink row-key schema/version (keys that include outServiceUid)
  2. Verify the stored byte array length matches the expected key size before constructing the row key
  3. Regenerate or repair affected rows; treat 1-3-byte-tail keys as legacy and fall back to the default service uid path
  4. Guard with buffer.remaining() >= BytesUtils.INT_BYTE_LENGTH before attempting to read the tail

Example fix

// before
UidLinkRowKey key = UidLinkRowKey.read(bytes);
// after
if (bytes == null || (bytes.length % keyUnit) != 0 || bytes.length < minKeyLength) {
    throw new IllegalArgumentException("unexpected UidLink key length: " + (bytes == null ? -1 : bytes.length));
}
UidLinkRowKey key = UidLinkRowKey.read(bytes);
Defensive patterns

Strategy: try-catch

Validate before calling

boolean isWellFormedUidLinkKey(byte[] rowKey, int expectedTailBytes) {
    return rowKey != null && rowKey.length >= expectedTailBytes;
}

Type guard

boolean isWellFormedUidLinkKey(byte[] rowKey, int expectedTailBytes) {
    return rowKey != null && rowKey.length >= expectedTailBytes;
}

Try / catch

try {
    long outServiceUid = rowKey.getOutServiceUid();
} catch (IllegalArgumentException e) {
    log.warn("truncated UidLink key, treating as legacy: {}", e.getMessage());
    long outServiceUid = ServiceUid.DEFAULT_SERVICE_UID_CODE; // legacy fallback
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/e04b03e35f638c30. Report an issue: GitHub.