apache/pulsar · error · IOException
The '${offloaderName}' offloader does not provide an offload
Error message
The '${offloaderName}' offloader does not provide an offloader factory implementation What it means
OffloaderUtils.getOffloaderFactory() reads the offloader's service definition YAML (META-INF services for PULSAR_OFFLOADER_SERVICE_NAME) and requires an offloaderFactoryClass entry. If the configured offloader plugin's definition lacks that field, an IOException is thrown. The driver jar is present but does not declare a factory implementation.
Source
Thrown at managed-ledger/src/main/java/org/apache/bookkeeper/mledger/offload/OffloaderUtils.java:66
* @return the offloader class name
* @throws IOException when fail to retrieve the pulsar offloader class
*/
static Pair<NarClassLoader, LedgerOffloaderFactory<?>> getOffloaderFactory(String narPath,
String narExtractionDirectory)
throws IOException {
// need to load offloader NAR to the classloader that also loaded LedgerOffloaderFactory in case
// LedgerOffloaderFactory is loaded by a classloader that is not the default classloader
NarClassLoader ncl = NarClassLoaderBuilder.builder()
.narFile(new File(narPath))
.parentClassLoader(LedgerOffloaderFactory.class.getClassLoader())
.extractionDirectory(narExtractionDirectory)
.build();
String configStr = ncl.getServiceDefinition(PULSAR_OFFLOADER_SERVICE_NAME);
OffloaderDefinition conf = ObjectMapperFactory.getYamlMapper().getObjectMapper()
.readValue(configStr, OffloaderDefinition.class);
if (StringUtils.isEmpty(conf.getOffloaderFactoryClass())) {
throw new IOException(
String.format("The '%s' offloader does not provide an offloader factory implementation",
conf.getName()));
}
try {
// Try to load offloader factory class and check it implements Offloader interface
Class<?> factoryClass = ncl.loadClass(conf.getOffloaderFactoryClass());
CompletableFuture<LedgerOffloaderFactory<?>> loadFuture = new CompletableFuture<>();
Thread loadingThread = new Thread(() -> {
Thread.currentThread().setContextClassLoader(ncl);
try {
Object offloader = factoryClass.getDeclaredConstructor().newInstance();
if (!(offloader instanceof LedgerOffloaderFactory)) {
throw new IOException("Class " + conf.getOffloaderFactoryClass() + " does not implement "
+ "interface " + LedgerOffloaderFactory.class.getName());
}
loadFuture.complete((LedgerOffloaderFactory<?>) offloader);
} catch (Throwable t) {View on GitHub (pinned to 820761864e)
Solutions
- Replace the offloader jar in the ./offloaders directory with a version matching your broker that declares offloaderFactoryClass
- Update custom offloader plugins to implement LedgerOffloaderFactory and ship the OffloaderDefinition YAML
- Verify with: unzip -p driver.jar META-INF/services/...yaml (check offloaderFactoryClass is set)
- Ensure only one offloader definition for the same name is on the classpath — conflicts can load the wrong definition
Example fix
// before (offloader-definition.yaml in legacy driver) name: my-s3 offloaderFactoryClass: # missing // after name: my-s3 offloaderFactoryClass: org.example.MyS3OffloaderFactory
Defensive patterns
Strategy: validation
Validate before calling
// pre-check the NAR's service definition before configuring
try (ZipFile z = new ZipFile(offloaderNar)) {
String yaml = readEntry(z, "META-INF/services/io.opensearch...yaml");
OffloaderDefinition d = YAML_MAPPER.readValue(yaml, OffloaderDefinition.class);
if (d.getOffloaderFactoryClass() == null || d.getOffloaderFactoryClass().isEmpty()) {
throw new IllegalStateException("offloader jar lacks offloaderFactoryClass: " + offloaderNar);
}
} Try / catch
try {
OffloaderUtils.getOffloaderFactory(ncl, conf, nrConf);
} catch (IOException e) {
log.error("Offloader jar missing factory impl — replace jar with a broker-compatible version", e);
} Prevention
- Keep ./offloaders jars at the same version as the broker
- After Pulsar upgrades, refresh all offloader plugins
- Inspect driver jar service definitions before deployment
- For custom plugins, implement LedgerOffloaderFactory and ship OffloaderDefinition YAML
When it happens
Trigger: Configuring a tiered-storage offloader whose driver jar contains an offloader definition YAML with an empty/missing offloaderFactoryClass — typically a legacy driver built for older Pulsar (pre-2.8) that used direct Offloader classes instead of OffloaderFactory, or a hand-rolled plugin missing the service definition.
Common situations: Upgrading Pulsar past 2.8 while keeping old offloader jars in ./offloaders; mixing driver versions (new driver jar with old, copied yaml); custom offloader plugin not updated to the factory SPI.
Related errors
- Class ${factoryClass} does not implement interface ${interfa
- Timeout during delete operation
- Timeout during close operation
- Timeout during open-cursor operation
- Timeout during delete-cursors operation
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/3213be0c1d99bfff.
Report an issue: GitHub.