apache/skywalking · error · IllegalArgumentException

Unmatched metrics data type, request for {}, but defined as

Error message

Unmatched metrics data type, request for {}, but defined as {}

What it means

Beyond existence, buildMetrics verifies that the dataType the caller requests equals the dataType the metric was created with (the generic T of the function's AcceptableValue). A mismatch throws 'Unmatched metrics data type, request for X, but defined as Y' — the prototype only produces values of the defined type, so accepting the request would corrupt the stream.

Source

Thrown at oap-server/server-core/src/main/java/org/apache/skywalking/oap/server/core/analysis/meter/MeterSystem.java:730

    /**
     * Create an {@link AcceptableValue} instance for streaming calculation. AcceptableValue instance is stateful,
     * shouldn't do {@link AcceptableValue#accept(MeterEntity, Object)} once it is pushed into {@link
     * #doStreamingCalculation(AcceptableValue)}.
     *
     * @param metricsName A defined metrics name. Use {@link #create(String, String, ScopeType, Class)} to define a new
     *                    one.
     * @param dataType    class type of the input of {@link AcceptableValue}
     * @return usable an {@link AcceptableValue} instance.
     */
    public <T> AcceptableValue<T> buildMetrics(String metricsName,
                                               Class<T> dataType) {
        MeterDefinition meterDefinition = meterPrototypes.get(metricsName);
        if (meterDefinition == null) {
            throw new IllegalArgumentException("Uncreated metrics " + metricsName);
        }
        if (!meterDefinition.getDataType().equals(dataType)) {
            throw new IllegalArgumentException(
                "Unmatched metrics data type, request for " + dataType.getName()
                    + ", but defined as " + meterDefinition.getDataType());
        }

        return meterDefinition.getMeterPrototype().createNew();
    }

    /**
     * Active the {@link MetricsStreamProcessor#in(Metrics)} for streaming calculation.
     *
     * @param acceptableValue should only be created through {@link #create(String, String, ScopeType, Class)}
     */
    public void doStreamingCalculation(AcceptableValue acceptableValue) {
        final long timeBucket = acceptableValue.getTimeBucket();
        if (timeBucket == 0L) {
            // Avoid no timestamp data, which could be harmful for the storage.
            acceptableValue.setTimeBucket(TimeBucket.getMinuteTimeBucket(System.currentTimeMillis()));
        }

View on GitHub (pinned to 102af09b4a)

Solutions

  1. Pass the same Class the metric was created with — for avgDouble/sumDouble metrics that is Double.class, for avg/sum it is Long.class, for histogram/percentile metrics BucketedValues.class / PercentileArgument
  2. If the type genuinely changed, remove and recreate the metric with the new function/type before resuming production

Example fix

// before
meterSystem.create("latency_avg", "avgDouble", ScopeType.SERVICE, Double.class);
AcceptableValue<Long> v = meterSystem.buildMetrics("latency_avg", Long.class);

// after
AcceptableValue<Double> v = meterSystem.buildMetrics("latency_avg", Double.class);
Defensive patterns

Strategy: type-guard

Validate before calling

// central registry of metric -> value type, filled at create() time
Map<String, Class<?>> metricTypes = new ConcurrentHashMap<>();
metricTypes.put(name, Double.class); // after create(name, "avgDouble", ..., Double.class)
Class<?> expected = metricTypes.get(name);
if (!expected.equals(dataType)) throw new IllegalStateException("Wrong type for " + name);

Type guard

boolean typeMatchesDefinition(String name, Class<?> dataType, Map<String, Class<?>> registry) {
    return dataType.equals(registry.get(name));
}

Try / catch

try {
    AcceptableValue<Double> v = meterSystem.buildMetrics(name, Double.class);
} catch (IllegalArgumentException e) {
    if (e.getMessage().startsWith("Unmatched metrics data type")) {
        throw new IllegalStateException("Type drift for " + name + ": " + e.getMessage(), e);
    }
    throw e;
}

Prevention

When it happens

Trigger: A rule or consumer calls buildMetrics(name, Long.class) while the metric was created with Double.class (e.g. avgDouble function), or requests a scalar type for a histogram metric created with BucketedValues.class; a refactor changed the function in the MAL rule but the calling code still passes the old type literal.

Common situations: Switching a MAL rule from avg to avgDouble at runtime while analyzer code keeps Long.class; custom exporters reading meter metrics; version upgrades that changed a function's value type.

Related errors


AI-assisted analysis of apache/skywalking@102af09b4a (2026-08-14). Data as JSON: /api/errors/f3c2b7cd41368307. Report an issue: GitHub.