kestra-io/kestra · error · IllegalArgumentException

Unsupported operation for QUERY filter: {filter.operation()}

Error message

Unsupported operation for QUERY filter: {filter.operation()}

What it means

The `SecretService.list()` method applies a `QUERY` filter from the caller. Only two operations are supported for this filter: `EQUALS` (substring contains, case-insensitive) and `NOT_EQUALS` (substring does not contain). If the filter's `operation()` is any other `QueryFilter.Op` value, an `IllegalArgumentException` is thrown. This is an API-consumer error indicating an unsupported query operation.

Source

Thrown at core/src/main/java/io/kestra/core/secret/SecretService.java:79

     * Finds the secret in full mode, as a value plus metadata.
     * The default returns the value with empty metadata. Multi-field secret managers override this to add metadata.
     */
    public SecretObject findSecretObject(String tenantId, String namespace, String key) throws SecretNotFoundException, IOException {
        return new SecretObject(findSecret(tenantId, namespace, key));
    }

    public ArrayListTotal<META> list(Pageable pageable, String tenantId, List<QueryFilter> filters) throws IOException {
        final Predicate<String> queryPredicate = filters.stream()
            .filter(filter -> QueryFilter.Field.QUERY.equals(filter.field()) && filter.value() != null)
            .findFirst()
            .map(filter ->
            {
                if (QueryFilter.Op.EQUALS.equals(filter.operation())) {
                    return (Predicate<String>) s -> Strings.CI.contains(s, (String) filter.value());
                } else if (QueryFilter.Op.NOT_EQUALS.equals(filter.operation())) {
                    return (Predicate<String>) s -> !Strings.CI.contains(s, (String) filter.value());
                } else {
                    throw new IllegalArgumentException("Unsupported operation for QUERY filter: " + filter.operation());
                }
            })
            .orElse(s -> true);

        //noinspection unchecked
        return ArrayListTotal.of(
            pageable,
            decodedSecrets.keySet().stream().filter(queryPredicate).map(s -> (META) s).toList()
        );
    }

    public Map<String, Set<String>> inheritedSecrets(String tenantId, String namespace) throws IOException {
        return Map.of(namespace, decodedSecrets.keySet());
    }

    public Map<String, Set<String>> ownAndInheritedSecrets(String tenantId, String namespace) throws IOException {
        return inheritedSecrets(tenantId, namespace);
    }

View on GitHub (pinned to 823fada927)

Solutions

  1. Use only `EQUALS` or `NOT_EQUALS` as the operation for a `QUERY` field filter when listing secrets.
  2. If you need prefix or suffix matching, filter the returned list client-side.
  3. If this is an SDK bug, report it — the API contract for secret listing should match.

Example fix

// before
filters.add(QueryFilter.of(QueryFilter.Field.QUERY, QueryFilter.Op.STARTS_WITH, "AWS"));
// after
filters.add(QueryFilter.of(QueryFilter.Field.QUERY, QueryFilter.Op.EQUALS, "AWS"));
Defensive patterns

Strategy: validation

Validate before calling

// Validate the query filter operation before calling list()
import io.kestra.core.models.QueryFilter;

public static void validateSecretQueryFilters(List<QueryFilter> filters) {
    for (QueryFilter f : filters) {
        if (QueryFilter.Field.QUERY.equals(f.field())) {
            if (f.operation() != QueryFilter.Op.EQUALS && f.operation() != QueryFilter.Op.NOT_EQUALS) {
                throw new IllegalArgumentException(
                    "QUERY filter only supports EQUALS and NOT_EQUALS, got: " + f.operation());
            }
        }
    }
}

Type guard

import { QueryFilter } from './types';

function isSupportedSecretQueryOp(op: QueryFilter.Op): boolean {
    return op === 'EQUALS' || op === 'NOT_EQUALS';
}

Try / catch

try {
    secretService.list(pageable, tenantId, filters);
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("Unsupported operation for QUERY filter")) {
        // strip unsupported filters and retry with EQUALS
        filters = filters.stream()
            .filter(f -> !QueryFilter.Field.QUERY.equals(f.field())
                || f.operation() == QueryFilter.Op.EQUALS
                || f.operation() == QueryFilter.Op.NOT_EQUALS)
            .toList();
        secretService.list(pageable, tenantId, filters);
    } else throw e;
}

Prevention

When it happens

Trigger: A REST API or SDK call to list secrets passes a `QueryFilter` with `field=QUERY` and an operation like `STARTS_WITH`, `GREATER_THAN`, or `IN`. The backend secret listing only supports substring matching.

Common situations: A frontend or SDK consumer tries to use a generic filter operation that is valid for other resource types but not implemented for secret listing. A version change introduced a new `QueryFilter.Op` enum value that is not yet handled here.

Related errors


AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14). Data as JSON: /api/errors/1b2d5d086b34a98a. Report an issue: GitHub.