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
- 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
- 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
- Record the dataType next to each metric name when creating it and always look it up for buildMetrics
- When changing a MAL function's value type, update every consumer in the same change
- Prefer typed helper methods (buildLongMetric/buildDoubleMetric) that encode the type once
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
- Uncreated metrics {}
- Expected String argument for extension function, got {}
- Expected number argument for extension function, got {}
- Expected number argument for extension function
- Expected list argument for extension function, got {}
AI-assisted analysis of apache/skywalking@102af09b4a (2026-08-14).
Data as JSON: /api/errors/f3c2b7cd41368307.
Report an issue: GitHub.