apache/hadoop · error · MetricsException

Unsupported counter type: {}

Error message

Unsupported counter type: {}

What it means

MethodMetric.newCounter builds the implementation for an @Metric-annotated method with type COUNTER; only int/Integer and long/Long return types are supported. Any other return type falls through to MetricsException('Unsupported counter type: <class name>').

Source

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

        return null;
    }
  }

  MutableMetric newCounter(final Class<?> type) {
    if (isInt(type) || isLong(type)) {
      return new MutableMetric() {
        @Override public void snapshot(MetricsRecordBuilder rb, boolean all) {
          try {
            Object ret = method.invoke(obj, (Object[])null);
            if (isInt(type)) rb.addCounter(info, ((Integer) ret).intValue());
            else rb.addCounter(info, ((Long) ret).longValue());
          } catch (Exception ex) {
            LOG.error("Error invoking method "+ method.getName(), ex);
          }
        }
      };
    }
    throw new MetricsException("Unsupported counter type: "+ type.getName());
  }

  static boolean isInt(Class<?> type) {
    boolean ret = type == Integer.TYPE || type == Integer.class;
    return ret;
  }

  static boolean isLong(Class<?> type) {
    return type == Long.TYPE || type == Long.class;
  }

  static boolean isFloat(Class<?> type) {
    return type == Float.TYPE || type == Float.class;
  }

  static boolean isDouble(Class<?> type) {
    return type == Double.TYPE || type == Double.class;
  }

View on GitHub (pinned to 2add963021)

Solutions

  1. Change the method's return type to int or long
  2. If the value is genuinely fractional, keep the metric a GAUGE instead of COUNTER
  3. Move the counter to an annotated MutableCounterLong field instead of a method

Example fix

// before
@Metric(type=Type.COUNTER)
public double filesRead() { return total; }

// after
@Metric(type=Type.COUNTER)
public long filesRead() { return total; }
Defensive patterns

Strategy: type-guard

Validate before calling

for (Method m : cls.getDeclaredMethods()) {
  Metric ann = m.getAnnotation(Metric.class);
  if (ann != null && ann.type() == Type.COUNTER && !isCounterType(m.getReturnType())) {
    throw new IllegalStateException("@Metric COUNTER on non-int/long method: " + m);
  }
}

Type guard

static boolean isCounterType(Class<?> t) {
  return t == int.class || t == Integer.class
      || t == long.class || t == Long.class;
}

Try / catch

try {
  new MetricsSourceBuilder(obj, "MyMetrics").build();
} catch (MetricsException e) {
  // check annotated method return types against isCounterType before build to avoid this
  throw e;
}

Prevention

When it happens

Trigger: @Metric(type=Type.COUNTER) on a method returning double, float, String, Boolean, or any type other than int/Integer/long/Long — MetricsSourceBuilder scans methods and delegates each to MethodMetric.

Common situations: Copy-pasting a gauge method (double) and flipping the annotation to COUNTER; refactoring a counter method's return type without updating the annotation; annotated methods returning boxed or custom types.

Related errors


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