pinpoint-apm/pinpoint · error · IllegalArgumentException

N:N relationship between fields and tags is not allowed.

Error message

N:N relationship between fields and tags is not allowed.

What it means

AppMetricDefinitionUtil.validateCountOfTagAndField enforces the cardinality rules for Pinpoint OTLP application-defined metrics: the relation between tag groups and fields must be 1:N (one tag group, many fields) or N:1 (many tag groups, one field). If both tagGroupList and fieldNameList have more than one element, the N:N case is rejected with IllegalArgumentException because the query/legend model cannot represent it.

Source

Thrown at otlpmetric/otlpmetric-common/src/main/java/com/navercorp/pinpoint/otlp/common/web/defined/AppMetricDefinitionUtil.java:42

import java.util.stream.Collectors;

/**
 * @author minwoo-jung
 */
public class AppMetricDefinitionUtil {

    static public void validate(List<AppMetricDefinition> appMetricDefinitionList) {
        for (AppMetricDefinition appMetricDefinition : appMetricDefinitionList) {
            List<String> tagGroupList = appMetricDefinition.getTagGroupList();
            List<String> fieldNameList = appMetricDefinition.getFieldNameList();

            validateCountOfTagAndField(tagGroupList, fieldNameList);
        }
    }

    static public void validateCountOfTagAndField(List<String> tagGroupList, List<String> fieldNameList) {
        if (tagGroupList.size() > 1 && fieldNameList.size() > 1) {
            throw new IllegalArgumentException("N:N relationship between fields and tags is not allowed.");
        }

        if (tagGroupList.size() != 1 && fieldNameList.size() != 1) {
            throw new IllegalArgumentException("Either tagGroupList or fieldNameList must have a size of exactly one.");
        }
    }

    static public void generateAndSetUniqueId(List<AppMetricDefinition> appMetricDefinitionList) {
        Set<String> existingIds = appMetricDefinitionList.stream()
                .map(AppMetricDefinition::getId)
                .filter(StringUtils::hasLength)
                .collect(Collectors.toSet());

        appMetricDefinitionList.stream().filter(appMetricDefinition -> StringUtils.isEmpty(appMetricDefinition.getId()))
                .forEach(definition -> {
                    String newId;

                    do {

View on GitHub (pinned to 744c3d3075)

Solutions

  1. Split the definition: create one AppMetricDefinition per (tagGroup, field) pair, keeping cardinality 1:N or N:1.
  2. Reduce the definition to multiple tag groups with a single field, or a single tag group with multiple fields.
  3. Validate the definition payload client-side before calling the save/validate API.
  4. If the N:N case is genuinely needed, model it as separate definitions or request a schema change upstream.

Example fix

// before
new AppMetricDefinition(tagGroups=["host","region"], fieldNames=["cpu","mem"], ...)
// after — one definition per pair
new AppMetricDefinition(tagGroups=["host"], fieldNames=["cpu","mem"], ...)
new AppMetricDefinition(tagGroups=["host","region"], fieldNames=["cpu"], ...)
Defensive patterns

Strategy: validation

Validate before calling

function isValidDefinition(def) {
  const tg = def.tagGroups?.length ?? 0;
  const fn = def.fieldNames?.length ?? 0;
  return !(tg > 1 && fn > 1) && (tg === 1 || fn === 1);
}
if (!isValidDefinition(payload)) throw new Error("Use 1:N or N:1 tag/field cardinality");

Type guard

boolean isOneToOneSide(List<String> tagGroups, List<String> fieldNames) {
    return !(tagGroups.size() > 1 && fieldNames.size() > 1)
        && (tagGroups.size() == 1 || fieldNames.size() == 1);
}

Try / catch

try {
    appMetricDefinitionService.save(def);
} catch (IllegalArgumentException e) {
    return badRequest("Metric definition cardinality invalid: " + e.getMessage());
}

Prevention

When it happens

Trigger: Saving or validating an AppMetricDefinition (via the web metric-definition API) where the definition declares multiple tagGroups AND multiple fieldNames simultaneously — e.g. a metric definition JSON with tagGroup ["a","b"] and fieldName ["f1","f2"].

Common situations: 1) User builds a metric definition in the Pinpoint web UI selecting several tag groupings and several fields at once. 2) Programmatically POSTing /api/otlp metric definitions with hand-written JSON where both lists have >1 entries. 3) Importing definitions exported from a different schema version.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of pinpoint-apm/pinpoint@744c3d3075 (2026-09-07). Data as JSON: /api/errors/4aca7f9b5083e19c. Report an issue: GitHub.