{"record":{"id":"92aadf82de0a33fe","repo":"apache/pulsar","slug":"unexpected-metadata-type-classname","errorCode":null,"errorMessage":"Unexpected metadata type: ${className}","messagePattern":"Unexpected metadata type: (.+?)","errorType":"exception","errorClass":"MetadataFormatException","httpStatus":null,"severity":"error","filePath":"pulsar-package-management/core/src/main/java/org/apache/pulsar/packages/management/core/common/PackageMetadataUtil.java","lineNumber":107,"sourceCode":"            return readLegacy(bytes);\n        }\n        throw new MetadataFormatException(\"Unrecognized package metadata format\");\n    }\n\n    private static PackageMetadata readJson(byte[] bytes) throws MetadataFormatException {\n        try {\n            return JSON_READER.readValue(bytes);\n        } catch (IOException e) {\n            throw new MetadataFormatException(\"Failed to parse package metadata as JSON: \" + e.getMessage());\n        }\n    }\n\n    private static PackageMetadata readLegacy(byte[] bytes) throws MetadataFormatException {\n        try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes))) {\n            ois.setObjectInputFilter(LEGACY_FILTER);\n            Object o = ois.readObject();\n            if (!(o instanceof PackageMetadata)) {\n                throw new MetadataFormatException(\"Unexpected metadata type: \"\n                        + (o == null ? \"null\" : o.getClass().getName()));\n            }\n            if (LEGACY_READ_WARNED.compareAndSet(false, true)) {\n                log.warn(\"Read a package metadata entry in the legacy Java serialization format. \"\n                        + \"Re-upload packages or call updateMeta to migrate them to JSON, then disable \"\n                        + \"packagesManagementAllowLegacyJavaSerialization.\");\n            }\n            return (PackageMetadata) o;\n        } catch (MetadataFormatException e) {\n            throw e;\n        } catch (Exception e) {\n            throw new MetadataFormatException(\"Rejected legacy package metadata: \" + e.getMessage());\n        }\n    }\n\n    private static int indexOfFirstNonWhitespace(byte[] bytes) {\n        for (int i = 0; i < bytes.length; i++) {\n            byte b = bytes[i];","sourceCodeStart":89,"sourceCodeEnd":125,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-package-management/core/src/main/java/org/apache/pulsar/packages/management/core/common/PackageMetadataUtil.java#L89-L125","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: reading stale serialized metadata directly\nPackageMetadata meta = PackageMetadataUtil.fromBytes(storedBytes);\n// after: re-upload/update metadata so it is stored as JSON\nmetadataService.updateMetadata(packageName, PackageMetadataUtil.toBytes(newMetadata));","handlingStrategy":"validation","validationCode":"static boolean looksLikeJsonOrEmpty(byte[] b) {\n    int i = 0;\n    while (i < b.length && Character.isWhitespace(b[i])) i++;\n    return i < b.length && (b[i] == '{' || b[i] == '[');\n}","typeGuard":"if (o instanceof PackageMetadata) { PackageMetadata m = (PackageMetadata) o; }","tryCatchPattern":"try {\n    return PackageMetadataUtil.fromBytes(bytes);\n} catch (MetadataFormatException e) {\n    log.warn(\"Unreadable package metadata, re-upload needed\", e);\n    throw new RestException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());\n}","preventionTips":["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."],"tags":["java","deserialization","serialization","package-management"],"backgroundTag":"java-deserialization-invalid-type","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}