apache/pulsar · error · IllegalArgumentException

Invalid txnId key:

Error message

Invalid txnId key: 

What it means

TxnIds.fromKey parses a transaction id key string of the form '<most>_<least>' back into a TxnID. It throws IllegalArgumentException when the key is not a well-formed pair of longs separated by exactly one underscore. This guards against corrupted or hand-crafted keys being silently mis-parsed.

Source

Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/transaction/metadata/TxnIds.java:49

 * single point that controls the on-the-wire encoding.
 */
public final class TxnIds {

    private static final char SEP = '_';

    /** @return {@code <most>_<least>}, suitable for use as a metadata-store path segment. */
    public static String toKey(TxnID txnId) {
        return txnId.getMostSigBits() + String.valueOf(SEP) + txnId.getLeastSigBits();
    }

    /**
     * @return the {@link TxnID} parsed from {@code key}.
     * @throws IllegalArgumentException if {@code key} is not in the expected {@code <most>_<least>} form
     */
    public static TxnID fromKey(String key) {
        int sep = key.indexOf(SEP);
        if (sep <= 0 || sep == key.length() - 1 || key.indexOf(SEP, sep + 1) >= 0) {
            throw new IllegalArgumentException("Invalid txnId key: " + key);
        }
        long most = Long.parseLong(key, 0, sep, 10);
        long least = Long.parseLong(key, sep + 1, key.length(), 10);
        return new TxnID(most, least);
    }

    private TxnIds() {}
}

View on GitHub (pinned to 820761864e)

Solutions

  1. Print and inspect the offending key string; ensure it is exactly '<most>_<least>' with numeric segments
  2. Use TxnIds.newKey(TxnID) (the pairing write path) to generate keys instead of building strings manually
  3. Strip any accidental prefix/suffix (e.g. topic or partition prefixes) before parsing
  4. If the key may be malformed, pre-validate with a regex like ^\d+_\d+$ before calling fromKey

Example fix

// before
TxnID txn = TxnIds.fromKey(userInput); // may throw
// after
if (userInput != null && userInput.matches("\\d+_\\d+")) {
    TxnID txn = TxnIds.fromKey(userInput);
} else {
    log.warn("Skipping malformed txnId key: {}", userInput);
}
Defensive patterns

Strategy: validation

Validate before calling

private static final java.util.regex.Pattern TXN_KEY = java.util.regex.Pattern.compile("\\d+_\\d+");
public static TxnID safeFromKey(String key) {
    if (key == null || !TXN_KEY.matcher(key).matches()) {
        return null; // or throw a domain-specific error
    }
    return TxnIds.fromKey(key);
}

Type guard

public static boolean isValidTxnKey(String key) {
    return key != null && key.matches("\\d+_\\d+");
}

Try / catch

try {
    TxnID txn = TxnIds.fromKey(key);
} catch (IllegalArgumentException e) {
    log.warn("Skipping malformed txnId key: {}", key, e);
    // skip or quarantine the key
}

Prevention

When it happens

Trigger: Calling TxnIds.fromKey with a string that has no underscore, an underscore at the start or end, more than one underscore, or non-numeric segments (which also throws NumberFormatException from Long.parseLong).

Common situations: Restoring/inspecting transaction metadata where keys were serialized by a different version or format; manually constructed keys in tests or tools; keys containing a trailing separator; using a ledger/bookkeeper entry id string instead of a txnId key.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/4b23842601d0de02. Report an issue: GitHub.