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
- Print and inspect the offending key string; ensure it is exactly '<most>_<least>' with numeric segments
- Use TxnIds.newKey(TxnID) (the pairing write path) to generate keys instead of building strings manually
- Strip any accidental prefix/suffix (e.g. topic or partition prefixes) before parsing
- 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
- Always generate keys via TxnIds.newKey / the library's pairing method instead of string concatenation
- Validate keys with a \d+_\d+ regex before parsing
- Beware Long.parseLong inside fromKey also throwing NumberFormatException on huge/overflowing segments
- Log the raw key value when reporting parse failures
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
- Invalid value %s for the position. Allowed values are [lates
- Invalid string to parse WorkerInfo : ${str}
- Invalid topic domain: '${value}'
- Expected TransactionV5, got: + txn.getClass()
- Invalid format for fully qualified instance name:
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/4b23842601d0de02.
Report an issue: GitHub.