apache/hadoop · error · IllegalArgumentException

Class-name comparators are not enabled (set tfile.comparator

Error message

Class-name comparators are not enabled (set tfile.comparator.jclass.enabled=true to allow): {comparator}

What it means

IllegalArgumentException from TFileMeta.makeComparator when the comparator name stored in the file starts with "jclass:" but the Configuration does not set tfile.comparator.jclass.enabled (default false). The comparator name comes from untrusted file metadata; instantiating an arbitrary class from it is a class-loading gadget, so the reflective path is opt-in. This guard only fires on files that declare a Java-class comparator.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/file/tfile/TFile.java:2100

    static BytesComparator makeComparator(String comparator) {
      return makeComparator(comparator, new Configuration());
    }

    @SuppressWarnings("unchecked")
    static BytesComparator makeComparator(String comparator,
        Configuration conf) {
      if (comparator.length() == 0) {
        // unsorted keys
        return null;
      }
      if (comparator.equals(COMPARATOR_MEMCMP)) {
        // default comparator
        return new BytesComparator(new MemcmpRawComparator());
      } else if (comparator.startsWith(COMPARATOR_JCLASS)) {
        if (!conf.getBoolean(TFILE_COMPARATOR_JCLASS_ENABLED,
            TFILE_COMPARATOR_JCLASS_ENABLED_DEFAULT)) {
          throw new IllegalArgumentException(
              "Class-name comparators are not enabled (set "
                  + TFILE_COMPARATOR_JCLASS_ENABLED + "=true to allow): "
                  + comparator);
        }
        String compClassName =
            comparator.substring(COMPARATOR_JCLASS.length()).trim();
        try {
          // Resolve without running the class initializer, confirm it really
          // is a RawComparator, and only then load and construct it.
          Class<?> compClass =
              Class.forName(compClassName, false, conf.getClassLoader());
          RawComparator<Object> rawComparator =
              (RawComparator<Object>) compClass.asSubclass(RawComparator.class)
                  .getDeclaredConstructor().newInstance();
          return new BytesComparator(rawComparator);
        } catch (Exception e) {
          throw new IllegalArgumentException(
              "Failed to instantiate comparator: " + comparator + "("

View on GitHub (pinned to 2add963021)

Solutions

  1. Only if you trust the file's origin: conf.setBoolean("tfile.comparator.jclass.enabled", true) on the exact Configuration handed to TFile.Reader/Writer.
  2. Prefer rewriting the data with the standard "memcmp" comparator so no reflective loading is ever needed.
  3. If jclass support is required fleet-wide, set the key in the job/cluster configuration and restrict who can write TFiles, since the flag re-opens a remote classloading vector.

Example fix

// before
Configuration conf = new Configuration();
TFile.Reader r = new TFile.Reader(in, len, conf); // IAE: jclass comparators disabled
// after
Configuration conf = new Configuration();
conf.setBoolean(TFile.TFILE_COMPARATOR_JCLASS_ENABLED, true); // trusted files only
TFile.Reader r = new TFile.Reader(in, len, conf);
Defensive patterns

Strategy: validation

Validate before calling

Configuration conf = new Configuration();
// only for files from a trusted producer that declares a jclass comparator
if (isTrustedProducer(path) && needsJClassComparator(path)) {
  conf.setBoolean(TFile.TFILE_COMPARATOR_JCLASS_ENABLED, true);
}
TFile.Reader r = new TFile.Reader(in, len, conf);

Try / catch

try {
  TFile.Reader r = new TFile.Reader(in, len, conf);
} catch (IllegalArgumentException e) {
  if (e.getMessage().contains("tfile.comparator.jclass.enabled")) { /* set flag if trusted, else rewrite file */ }
  else throw e;
}

Prevention

When it happens

Trigger: Opening a TFile written with a 'jclass:com.foo.MyComparator' comparator while the reader's Configuration leaves tfile.comparator.jclass.enabled unset/false. Both the Writer constructor and the Reader path run makeComparator, so writing such a file needs the same flag.

Common situations: Default-hardened clusters reading older pre-hardening files that embedded jclass comparators; jobs where the writer node had the flag set but the reader job's configuration does not; security review forcing the flag off and legacy files failing immediately after.

Related errors


AI-assisted analysis of apache/hadoop@2add963021 (2026-08-22). Data as JSON: /api/errors/6492dec91e464a43. Report an issue: GitHub.