apache/pulsar · error · UnsupportedOperationException

Metadata cache implementation ${className} not supported by

Error message

Metadata cache implementation ${className} not supported by FaultInjectionMetadataStore

What it means

FaultInjectionMetadataStore is a test wrapper that must inject itself into every MetadataCache it hands out, replacing the cache's internal `store` field. It can only do this for MetadataCacheImpl instances; for any other MetadataCache implementation it throws UnsupportedOperationException naming the class, after reflectively overwriting the field via FieldUtils.

Source

Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/impl/FaultInjectionMetadataStore.java:193

    @Override
    public <T> MetadataCache<T> getMetadataCache(TypeReference<T> typeRef, MetadataCacheConfig<?> cacheConfig) {
        return injectMetadataStoreInMetadataCache(store.getMetadataCache(typeRef, cacheConfig));
    }

    @SuppressWarnings("deprecation")
    @Override
    public <T> MetadataCache<T> getMetadataCache(String cacheName, MetadataSerde<T> serde,
                                                 MetadataCacheConfig<?> cacheConfig) {
        return injectMetadataStoreInMetadataCache(store.getMetadataCache(serde, cacheConfig));
    }

    @SneakyThrows
    private <T> MetadataCache<T> injectMetadataStoreInMetadataCache(MetadataCache<T> metadataCache) {
        if (metadataCache instanceof MetadataCacheImpl) {
            FieldUtils.writeField(metadataCache, "store", this, true);
        } else {
            throw new UnsupportedOperationException("Metadata cache implementation "
                    + metadataCache.getClass().getName() + " not supported by FaultInjectionMetadataStore");
        }
        return metadataCache;
    }

    @Override
    public void registerSessionListener(Consumer<SessionEvent> listener) {
        store.registerSessionListener(listener);
        sessionListeners.add(listener);
    }

    @Override
    public CompletableFuture<Void> handleMetadataEvent(MetadataEvent event) {
        return store.handleMetadataEvent(event);
    }

    @Override
    public void close() throws Exception {

View on GitHub (pinned to 820761864e)

Solutions

  1. Use a metadata store whose getMetadataCache returns standard MetadataCacheImpl instances (e.g., the built-in ZK/Oxia/RocksDB stores)
  2. Update the custom MetadataCache implementation to extend MetadataCacheImpl
  3. Only wrap stores with FaultInjectionMetadataStore that are known-compatible in tests

Example fix

// before
FaultInjectionMetadataStore faultStore = new FaultInjectionMetadataStore(customStore); // custom cache
// after
FaultInjectionMetadataStore faultStore = new FaultInjectionMetadataStore(new OxiaMetadataStore(cfg));
Defensive patterns

Strategy: type-guard

Validate before calling

// Java: only wrap stores whose caches are standard impls
MetadataCache<?> cache = store.getMetadataCache(String.class);
if (!(cache instanceof MetadataCacheImpl)) {
    throw new IllegalStateException("Store not compatible with FaultInjectionMetadataStore");
}

Type guard

static boolean supportsFaultInjection(MetadataCache<?> cache) {
    return cache instanceof MetadataCacheImpl;
}

Try / catch

try {
    MetadataCache<T> c = faultStore.getMetadataCache(String.class);
} catch (UnsupportedOperationException e) {
    // store returns a non-standard cache; switch to a built-in store in tests
}

Prevention

When it happens

Trigger: Calling FaultInjectionMetadataStore.getMetadataCache() when the underlying store returns a non-MetadataCacheImpl cache implementation — typically a custom or third-party MetadataCache subclass.

Common situations: Using FaultInjectionMetadataStore in tests with a custom metadata store/cache implementation that does not extend MetadataCacheImpl.

Related errors


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