apache/pulsar · error · BookieException.BookieIllegalOpException
Invalid version type, expected it to be LongVersion
Error message
Invalid version type, expected it to be LongVersion
What it means
PulsarRegistrationManager.writeCookie implements BookKeeper's RegistrationManager contract using Pulsar's MetadataStore, which uses optimistic-concurrency Long versions (like ZooKeeper zxid-based versions). Before writing a cookie it must convert the Version on the cookie's Versioned payload to a LongVersion; if the caller passes a cookie whose version is neither Version.NEW nor a LongVersion instance, the manager cannot map it to a store version and throws BookieException.BookieIllegalOpException. This typically means the Versioned cookie object was constructed with an incompatible Version implementation (e.g. a version read from a different metadata backend or a hand-built/stale Versioned object).
Source
Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarRegistrationManager.java:231
throw new BookieException.MetadataStoreException(e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.error().attr("bookieId", bookieId).exception(e)
.log("InterruptedException while checking registration ephemeral nodes");
throw new BookieException.MetadataStoreException(e);
}
}
@Override
public void writeCookie(BookieId bookieId, Versioned<byte[]> cookieData) throws BookieException {
String path = this.cookiePath + "/" + bookieId;
try {
long version;
if (Version.NEW == cookieData.getVersion()) {
version = -1L;
} else {
if (!(cookieData.getVersion() instanceof LongVersion)) {
throw new BookieException.BookieIllegalOpException(
"Invalid version type, expected it to be LongVersion");
}
version = ((LongVersion) cookieData.getVersion()).getLongVersion();
}
store.put(path, cookieData.getValue(), Optional.of(version))
.get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new BookieException.MetadataStoreException("Interrupted writing cookie for bookie " + bookieId, ie);
} catch (ExecutionException e) {
if (e.getCause() instanceof MetadataStoreException.BadVersionException) {
throw new BookieException.CookieExistException(bookieId.toString());
} else {
throw new BookieException.MetadataStoreException("Failed to write cookie for bookie " + bookieId);
}
} catch (TimeoutException ex) {
throw new BookieException.MetadataStoreException("Failed to write cookie for bookie " + bookieId, ex);View on GitHub (pinned to 820761864e)
Solutions
- Obtain the cookie via this manager's readCookie(bookieId), which always returns a LongVersion version; use its version when writing.
- If the cookie is new, pass Versioned.value(cookieBytes) so getVersion() is Version.NEW and the write uses create semantics (version -1).
- Wrap any externally obtained version in new LongVersion(((AbstractVersion) v).getLongVersion())-style conversion, or re-read the cookie from the store to get the correct LongVersion before writing.
- Audit any custom code that constructs Versioned<byte[]> cookies and ensure it does not attach foreign Version implementations (e.g. zk Stat versions).
Example fix
// before
Versioned<byte[]> cookie = new Versioned<>(data, someZkBasedVersion);
regManager.writeCookie(bookieId, cookie);
// after
Optional<GetResult> existing = store.get(cookiePath + "/" + bookieId).get();
Version version = existing.isPresent()
? new LongVersion(existing.get().getStat().getVersion())
: Version.NEW;
regManager.writeCookie(bookieId, new Versioned<>(data, version)); Defensive patterns
Strategy: validation
Validate before calling
Version v = cookieData.getVersion();
if (!(Version.NEW == v || v instanceof LongVersion)) {
throw new IllegalArgumentException(
"Cookie version must be Version.NEW or LongVersion, got: " + v.getClass().getName());
} Type guard
static boolean isWritableCookieVersion(Versioned<byte[]> cookie) {
return cookie != null
&& (Version.NEW == cookie.getVersion()
|| cookie.getVersion() instanceof LongVersion);
} Try / catch
try {
regManager.writeCookie(bookieId, cookie);
} catch (BookieException.BookieIllegalOpException e) {
// version object came from another backend; re-read cookie to get LongVersion
Versioned<byte[]> fresh = regManager.readCookie(bookieId);
regManager.writeCookie(bookieId, new Versioned<>(cookie.getValue(), fresh.getVersion()));
} Prevention
- Always source cookie Version objects from the same RegistrationManager/MetadataStore you write through (readCookie returns LongVersion).
- Never carry Versioned cookies across different metadata backends or cache them across metadata service migrations.
- When constructing new cookies, use Versioned.value(bytes) so the version is Version.NEW.
- Add an instanceof LongVersion check in any custom cookie-management code before calling writeCookie.
When it happens
Trigger: Calling writeCookie(bookieId, cookieData) where cookieData.getVersion() is not Version.NEW and not an instance of org.apache.pulsar.metadata.api.LongVersion — e.g. passing a Versioned<byte[]> whose version came from another RegistrationManager implementation, or one constructed programmatically with a custom/null Version rather than obtained from readCookie() on this manager.
Common situations: Mixing metadata backends: a cookie read under ZooKeeper-based BookKeeper metadata and replayed against a Pulsar MetadataStore-backed manager; a bookie's cookie cache carrying stale Version objects after re-registering against a different metadata service; custom bookie startup/fencing code that fabricates a Versioned cookie instead of calling readCookie; tests building Versioned<byte[]> with mock Version implementations.
Related errors
- Interrupted writing cookie for bookie ${bookieId}
- Failed to write cookie for bookie ${bookieId}
- Interrupted deleting cookie for bookie ${bookieId}
- Failed to delete cookie for bookie ${bookieId}
- METADATA_SERVICE_ERROR
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/d71ae0c376396002.
Report an issue: GitHub.