apache/druid · warning · java.lang.UnsupportedOperationException

CLEANER_NOT_SUPPORTED_EXCEPTION

CLEANER_NOT_SUPPORTED_EXCEPTION

Error message

Cleaning is not support on this platform, because internal Java APIs are not compatible with this Druid version

What it means

Cleaners.register throws this UnsupportedOperationException when the CLEANER static handle is null, i.e. the platform never successfully initialized a cleaner. The constant CLEANER_NOT_SUPPORTED_EXCEPTION carries the same 'internal Java APIs are not compatible' message, telling callers that explicit cleanup action registration is unavailable.

Solutions

  1. Check Cleaners.isAssigned() (or equivalent) before registering cleaning actions and skip registration when unsupported.
  2. Ensure your cleanup logic also runs via explicit close paths, not only via cleaners.
  3. Run on a supported JDK so the cleaner initializes.

Example fix

// before
Cleaners.register(obj, cleanup);
// after
if (Cleaners.isAssigned()) {
  Cleaners.register(obj, cleanup);
} else {
  // cleanup handled by close()/GC
}
Defensive patterns

Strategy: type-guard

Type guard

boolean canRegisterCleaner() { return Cleaners.isAssigned(); }

Try / catch

try {
  Cleaners.register(obj, action);
} catch (UnsupportedOperationException e) {
  // CLEANER not initialized; fall back
}

Prevention

When it happens

Trigger: Any call to Cleaners.register(object, runnable) after Cleaners failed to initialize (takeMeToTheCleaners threw, leaving CLEANER null), which happens on JVMs with incompatible internal cleaning APIs.

Common situations: Running on non-standard JVMs or locked-down JDKs (see related init failures); using Cleaners-based resource management in extensions deployed to such runtimes.

Related errors


AI-assisted analysis of apache/druid@9b90983fd2 (2026-09-07). Data as JSON: /api/errors/5aef57619b2b753e. Report an issue: GitHub.

Appendix: source

Thrown at processing/src/main/java/org/apache/druid/java/util/common/Cleaners.java:100

    catch (Throwable t) {
      throw new RuntimeException("Unable to create cleaner", t);
    }

    MethodHandle register = lookup.findVirtual(
        cleaner,
        "register",
        MethodType.methodType(cleanable, Object.class, Runnable.class)
    ).bindTo(theCleaner);

    MethodHandle clean = lookup.findVirtual(cleanable, "clean", MethodType.methodType(void.class));

    return new CleanerImpl(register, clean);
  }

  public static Cleanable register(Object object, Runnable runnable)
  {
    if (CLEANER == null) {
      throw new UnsupportedOperationException(CLEANER_NOT_SUPPORTED_EXCEPTION);
    }

    return CLEANER.register(object, runnable);
  }

  private static class CleanerImpl implements Cleaner
  {
    private final MethodHandle register;
    private final MethodHandle clean;

    private CleanerImpl(MethodHandle register, MethodHandle clean)
    {
      this.register = register;
      this.clean = clean;
    }

    @Override
    public Cleanable register(Object object, Runnable runnable)

View on GitHub (pinned to 9b90983fd2)