apache/hadoop · error · MetricsException

Unsupported metric field {} of type {}

Error message

Unsupported metric field {} of type {}

What it means

When a metrics source class is initialized, Hadoop metrics2's MutableMetricsFactory reflectively scans every field annotated with @Metric and instantiates a Mutable metric based on the field's exact declared type. Only MutableCounterInt, MutableCounterLong, MutableGaugeInt, MutableGaugeLong, MutableGaugeFloat, MutableRate, MutableRates, MutableRatesWithAggregation, MutableStat, MutableRollingAverages and MutableQuantiles are recognized (matching is exact Class equality). Any other declared type falls through to this MetricsException, naming the offending field and its type, and fails source registration at startup.

Source

Thrown at hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/metrics2/lib/MutableMetricsFactory.java:90

      return new MutableRates(registry);
    }
    if (cls == MutableRatesWithAggregation.class) {
      return registry.newRatesWithAggregation(info.name());
    }
    if (cls == MutableStat.class) {
      return registry.newStat(info.name(), info.description(),
                              annotation.sampleName(), annotation.valueName(),
                              annotation.always());
    }
    if (cls == MutableRollingAverages.class) {
      return registry.newMutableRollingAverages(info.name(),
          annotation.valueName());
    }
    if (cls == MutableQuantiles.class) {
      return registry.newQuantiles(info.name(), annotation.about(),
          annotation.sampleName(), annotation.valueName(), annotation.interval());
    }
    throw new MetricsException("Unsupported metric field "+ field.getName() +
                               " of type "+ field.getType().getName());
  }

  MutableMetric newForMethod(Object source, Method method, Metric annotation,
                             MetricsRegistry registry) {
    if (LOG.isDebugEnabled()) {
      LOG.debug("method "+ method +" with annotation "+ annotation);
    }
    MetricsInfo info = getInfo(annotation, method);
    MutableMetric metric = newForMethod(source, method, annotation);
    metric = metric != null ? metric :
        new MethodMetric(source, method, info, annotation.type());
    registry.add(info.name(), metric);
    return metric;
  }

  /**
   * Override to handle custom mutable metrics for fields

View on GitHub (pinned to 2add963021)

Solutions

  1. Change the field's declared type to a supported class, e.g. MutableCounterLong, MutableGaugeLong, MutableStat, MutableRate, MutableQuantiles or MutableRollingAverages
  2. For a genuinely custom metric type, override MutableMetricsFactory#newForField(Field, Metric) (the hook exists exactly for custom mutable metrics) instead of relying on the type map
  3. If the registry pattern is not needed, implement MetricsSource directly and emit MetricsRecords without @Metric fields

Example fix

// before
@Metrics(about="Job metrics", context="myjob")
public class MyMetrics {
  @Metric MutableMetric requests; // Unsupported metric field requests of type ...
}

// after
@Metrics(about="Job metrics", context="myjob")
public class MyMetrics {
  @Metric MutableCounterLong requests;
}
Defensive patterns

Strategy: type-guard

Validate before calling

Set<Class<?>> SUPPORTED = new HashSet<>(Arrays.asList(
    MutableCounterInt.class, MutableCounterLong.class,
    MutableGaugeInt.class, MutableGaugeLong.class, MutableGaugeFloat.class,
    MutableRate.class, MutableRates.class, MutableRatesWithAggregation.class,
    MutableStat.class, MutableRollingAverages.class, MutableQuantiles.class));
for (Field f : MyMetrics.class.getDeclaredFields()) {
  if (f.isAnnotationPresent(Metric.class) && !SUPPORTED.contains(f.getType())) {
    throw new IllegalStateException("Bad @Metric field " + f.getName()
        + " of type " + f.getType().getName());
  }
}

Type guard

static boolean isSupportedMetricField(Field f) {
  if (!f.isAnnotationPresent(Metric.class)) return true;
  Class<?> t = f.getType();
  return t == MutableCounterInt.class || t == MutableCounterLong.class
      || t == MutableGaugeInt.class || t == MutableGaugeLong.class
      || t == MutableGaugeFloat.class || t == MutableRate.class
      || t == MutableRates.class || t == MutableRatesWithAggregation.class
      || t == MutableStat.class || t == MutableRollingAverages.class
      || t == MutableQuantiles.class;
}

Try / catch

try {
  metricsSystem.register("myjob", "My job metrics", new MyMetrics());
} catch (MetricsException e) {
  // message names the exact unsupported field and its type
  throw new IllegalStateException("Metrics source registration failed: " + e.getMessage(), e);
}

Prevention

When it happens

Trigger: Declaring @Metric on a field whose type is not in the supported list: @Metric MutableMetric requests; @Metric int counter; or @Metric on a custom MutableMetric subclass (exact class comparison means subclasses do NOT match). Thrown from newForField during MetricsSystem.register() or annotation-driven init of a @Metrics class.

Common situations: Writing a custom MetricsSource and declaring the interface type instead of a concrete class; porting code between Hadoop versions where a Mutable* type (e.g. MutableQuantiles, MutableRollingAverages, MutableRatesWithAggregation) does not exist so a hand-written stand-in is used; importing a same-named class from another package so the Class identity check fails.

Related errors


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