halo-dev/halo · error · IllegalArgumentException

Unsupported operator: {}

Error message

Unsupported operator: {}

What it means

Thrown by FieldSelectorConverter.convert when the SelectorCriteria operator is not one of Equals, NotEquals, or IN (the only operators the field-selector path handles). The Operator enum also defines Exist and NotExist, which are valid for label selectors but have no field-selector query, so a field selector using bare-key existence (`fieldKey`) or negated existence (`!fieldKey`) hits the default branch and throws.

Source

Thrown at api/src/main/java/run/halo/app/extension/router/selector/FieldSelectorConverter.java:32

    public Condition convert(SelectorCriteria criteria) {
        var key = criteria.key();
        // compatible with old field selector
        if ("name".equals(key)) {
            key = "metadata.name";
        }
        switch (criteria.operator()) {
            case Equals -> {
                return Queries.equal(key, getSingleValue(criteria));
            }
            case NotEquals -> {
                return Queries.notEqual(key, getSingleValue(criteria));
            }
            // compatible with old field selector
            case IN -> {
                Set<String> valueArr = defaultIfNull(criteria.values(), Set.of());
                return Queries.in(key, valueArr);
            }
            default -> throw new IllegalArgumentException("Unsupported operator: " + criteria.operator());
        }
    }

    String getSingleValue(SelectorCriteria criteria) {
        if (CollectionUtils.isEmpty(criteria.values())) {
            throw new IllegalArgumentException("No value present for label key: " + criteria.key());
        }
        return criteria.values().iterator().next();
    }
}

View on GitHub (pinned to d2f5165f9c)

Solutions

  1. For field selectors, use only value operators: key=value, key!=value, or key in (a,b). Move existence checks to labelSelector where Exist/NotExist are supported.
  2. If you construct SelectorCriteria programmatically, restrict the operator to Equals/NotEquals/IN for field selectors.
  3. Upgrade both api and application modules together so a new operator is handled on both converter paths.

Example fix

// before
GET /apis?fieldSelector=name

// after
GET /apis?labelSelector=app%3Dhalo   // use label existence, or
GET /apis?fieldSelector=name=in%20(a,b)
Defensive patterns

Strategy: validation

Validate before calling

Set<Operator> supported = EnumSet.of(Operator.Equals, Operator.NotEquals, Operator.IN);
if (!supported.contains(criteria.operator())) {
    throw new IllegalArgumentException(
        "Field selector supports only =, !=, and in; got " + criteria.operator());
}

Type guard

boolean fieldSupported = switch (criteria.operator()) {
    case Equals, NotEquals, IN -> true;
    default -> false;
};

Try / catch

try {
    Condition cond = new FieldSelectorConverter().convert(criteria);
} catch (IllegalArgumentException e) {
    // re-educate the caller: field selectors accept only value operators
}

Prevention

When it happens

Trigger: A `?fieldSelector=name` (Exist) or `?fieldSelector=!status` (NotExist) query reaches FieldSelectorConverter; or a future Operator enum constant is added without a case here.

Common situations: A client reuses label-selector syntax (bare key for existence) on a fieldSelector query; an SDK builds a SelectorCriteria with Exist/NotExist against field keys; upgrading Halo adds an operator not yet mapped.

Related errors


AI-assisted analysis of halo-dev/halo@d2f5165f9c (2026-08-14). Data as JSON: /api/errors/40ee3e68fde5b54c. Report an issue: GitHub.