pinpoint-apm/pinpoint · error · IllegalArgumentException

agentIndex not found:${transactionId}

Error message

agentIndex not found:${transactionId}

What it means

TransactionIdUtils.parseTransactionId parses the Pinpoint transactionId string format agentId^agentStartTime^transactionSequence (delimiter-separated). If nextIndex finds no delimiter at position 0, the string has no separator and cannot contain an agentId, so an IllegalArgumentException with 'agentIndex not found:<id>' is thrown.

Source

Thrown at commons-profiler/src/main/java/com/navercorp/pinpoint/common/profiler/util/TransactionIdUtils.java:89

        final byte[] buffer = new byte[bufferSize];
        buffer[0] = VERSION;
        int offset = VERSION_SIZE;
        // write prefix String
        offset = BytesUtils.writeVar32(zigZagAgentIdLength, buffer, offset);
        if (agentIdBytes != null) {
            offset = BytesUtils.writeBytes(buffer, offset, agentIdBytes);
        }
        offset = BytesUtils.writeVar64(agentStartTime, buffer, offset);
        BytesUtils.writeVar64(transactionSequence, buffer, offset);
        return buffer;
    }

    public static TransactionId parseTransactionId(final String transactionId) {
        Objects.requireNonNull(transactionId, "transactionId");

        final int agentIdIndex = nextIndex(transactionId, 0);
        if (agentIdIndex == -1) {
            throw new IllegalArgumentException("agentIndex not found:" + transactionId);
        }
        if (!IdValidateUtils.checkId(transactionId, 0, agentIdIndex)) {
            throw new IllegalArgumentException("invalid transactionId:" + transactionId);
        }
        final String agentId = transactionId.substring(0, agentIdIndex);

        final int agentStartTimeIndex = nextIndex(transactionId, agentIdIndex + 1);
        if (agentStartTimeIndex == -1) {
            throw new IllegalArgumentException("agentStartTimeIndex not found:" + transactionId);
        }
        final long agentStartTime = parseLong(transactionId, agentIdIndex + 1, agentStartTimeIndex);

        int transactionSequenceIndex = nextIndex(transactionId, agentStartTimeIndex + 1);
        if (transactionSequenceIndex == -1) {
            // next index may not exist since default value does not have a delimiter after transactionSequence.
            // may need fixing when id spec changes
            transactionSequenceIndex = transactionId.length();
        }

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Print/inspect the offending string and confirm it uses the expected delimiter-separated format (agentId^agentStartTime^transactionSequence).
  2. URL-decode or un-escape the transactionId before parsing if it came from a web request or query string.
  3. Only pass strings produced by TransactionIdUtils.formatTransactionId; validate the input (contains the delimiter, correct number of segments) before calling parseTransactionId.

Example fix

// before
TransactionId id = TransactionIdUtils.parseTransactionId(requestParam);
// after
if (transactionId.contains(TransactionIdUtils.TRANSACTION_ID_DELIMITER)) {
    TransactionId id = TransactionIdUtils.parseTransactionId(requestParam);
} else {
    throw new IllegalArgumentException("Not a formatted pinpoint transactionId: " + requestParam);
}
Defensive patterns

Strategy: validation

Validate before calling

boolean isParseable(String id) {
    return id != null && !id.isEmpty()
        && id.indexOf(TransactionIdUtils.TRANSACTION_ID_DELIMITER) > 0;
}
// call only if isParseable(rawId)

Try / catch

try {
    TransactionId id = TransactionIdUtils.parseTransactionId(rawId);
} catch (IllegalArgumentException e) {
    if (e.getMessage() != null && e.getMessage().startsWith("agentIndex not found")) {
        logger.warn("Malformed transactionId (missing delimiter): {}", rawId);
    } else { throw e; }
}

Prevention

When it happens

Trigger: Passing a transactionId string that contains no delimiter (the first nextIndex() over the default separator returns -1), e.g. calling TransactionIdUtils.parseTransactionId("abcdef1234") or passing a UUID/timestamp instead of a Pinpoint-encoded transactionId.

Common situations: Parsing user-supplied or truncated transaction ids from logs/UI links that were URL-encoded or mangled (e.g. the '^' delimiter lost by shell, HTML or CSV processing); passing an internal numeric id where the formatted string was expected; version mismatch where an older/newer transactionId format is fed to this parser.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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