apache/pulsar · error · MetadataFormatException
Unexpected metadata type: ${className}
Error message
Unexpected metadata type: ${className} What it means
PackageMetadataUtil.readLegacy deserializes package metadata stored in the legacy Java serialization format. After applying a restrictive ObjectInputFilter (LEGACY_FILTER), it verifies the deserialized object is actually a PackageMetadata instance; anything else (or null) means the stored bytes are not valid legacy metadata, so MetadataFormatException is thrown. The library supports legacy Java serialization only as a migration path, gated behind the packagesManagementAllowLegacyJavaSerialization flag.
Source
Thrown at pulsar-package-management/core/src/main/java/org/apache/pulsar/packages/management/core/common/PackageMetadataUtil.java:107
return readLegacy(bytes);
}
throw new MetadataFormatException("Unrecognized package metadata format");
}
private static PackageMetadata readJson(byte[] bytes) throws MetadataFormatException {
try {
return JSON_READER.readValue(bytes);
} catch (IOException e) {
throw new MetadataFormatException("Failed to parse package metadata as JSON: " + e.getMessage());
}
}
private static PackageMetadata readLegacy(byte[] bytes) throws MetadataFormatException {
try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {
ois.setObjectInputFilter(LEGACY_FILTER);
Object o = ois.readObject();
if (!(o instanceof PackageMetadata)) {
throw new MetadataFormatException("Unexpected metadata type: "
+ (o == null ? "null" : o.getClass().getName()));
}
if (LEGACY_READ_WARNED.compareAndSet(false, true)) {
log.warn("Read a package metadata entry in the legacy Java serialization format. "
+ "Re-upload packages or call updateMeta to migrate them to JSON, then disable "
+ "packagesManagementAllowLegacyJavaSerialization.");
}
return (PackageMetadata) o;
} catch (MetadataFormatException e) {
throw e;
} catch (Exception e) {
throw new MetadataFormatException("Rejected legacy package metadata: " + e.getMessage());
}
}
private static int indexOfFirstNonWhitespace(byte[] bytes) {
for (int i = 0; i < bytes.length; i++) {
byte b = bytes[i];View on GitHub (pinned to 820761864e)
Solutions
- Re-upload the package or call updateMeta to rewrite the metadata in JSON format, then disable packagesManagementAllowLegacyJavaSerialization.
- Check what wrote the metadata blob; ensure it was written by Pulsar's package management service.
- If legacy reads are needed, confirm packagesManagementAllowLegacyJavaSerialization=true and that the stored class is org.apache.pulsar.packages.management.core.common.PackageMetadata.
- Delete the corrupt package metadata entry and re-create the package.
Example fix
// before: reading stale serialized metadata directly PackageMetadata meta = PackageMetadataUtil.fromBytes(storedBytes); // after: re-upload/update metadata so it is stored as JSON metadataService.updateMetadata(packageName, PackageMetadataUtil.toBytes(newMetadata));
Defensive patterns
Strategy: validation
Validate before calling
static boolean looksLikeJsonOrEmpty(byte[] b) {
int i = 0;
while (i < b.length && Character.isWhitespace(b[i])) i++;
return i < b.length && (b[i] == '{' || b[i] == '[');
} Type guard
if (o instanceof PackageMetadata) { PackageMetadata m = (PackageMetadata) o; } Try / catch
try {
return PackageMetadataUtil.fromBytes(bytes);
} catch (MetadataFormatException e) {
log.warn("Unreadable package metadata, re-upload needed", e);
throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());
} Prevention
- Keep packagesManagementAllowLegacyJavaSerialization=false unless migrating and re-upload/updateMeta legacy entries promptly.
- Never hand-edit metadata blobs in package storage.
- Verify metadata is written through the package management service, not external tools.
- Alert on 'legacy Java serialization format' warnings — they mark entries needing migration.
When it happens
Trigger: fromBytes() is called with bytes that are neither JSON nor a Java-serialized PackageMetadata; e.g. the stored blob was written by a different class, was corrupted/truncated, or deserializeObject yielded a different type such as a String or HashMap.
Common situations: Upgrading Pulsar from versions where metadata was Java-serialized; someone hand-edited or replaced the metadata blob in storage; a different tool wrote metadata into the package storage; deserialization filter rejected the object causing readObject to produce an unexpected type or null.
Related errors
- Rejected legacy package metadata:
- Failed to deserialize LongBitmap
- ${key} already exists in the dynamicConfigurationMap
- Topic factory failed to create topic
- No more range can assigned to new consumer, assigned consume
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/92aadf82de0a33fe.
Report an issue: GitHub.