{"record":{"id":"1b2d5d086b34a98a","repo":"kestra-io/kestra","slug":"unsupported-operation-for-query-filter-filter-op","errorCode":null,"errorMessage":"Unsupported operation for QUERY filter: {filter.operation()}","messagePattern":"Unsupported operation for QUERY filter: (.+?)","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"core/src/main/java/io/kestra/core/secret/SecretService.java","lineNumber":79,"sourceCode":"     * Finds the secret in full mode, as a value plus metadata.\n     * The default returns the value with empty metadata. Multi-field secret managers override this to add metadata.\n     */\n    public SecretObject findSecretObject(String tenantId, String namespace, String key) throws SecretNotFoundException, IOException {\n        return new SecretObject(findSecret(tenantId, namespace, key));\n    }\n\n    public ArrayListTotal<META> list(Pageable pageable, String tenantId, List<QueryFilter> filters) throws IOException {\n        final Predicate<String> queryPredicate = filters.stream()\n            .filter(filter -> QueryFilter.Field.QUERY.equals(filter.field()) && filter.value() != null)\n            .findFirst()\n            .map(filter ->\n            {\n                if (QueryFilter.Op.EQUALS.equals(filter.operation())) {\n                    return (Predicate<String>) s -> Strings.CI.contains(s, (String) filter.value());\n                } else if (QueryFilter.Op.NOT_EQUALS.equals(filter.operation())) {\n                    return (Predicate<String>) s -> !Strings.CI.contains(s, (String) filter.value());\n                } else {\n                    throw new IllegalArgumentException(\"Unsupported operation for QUERY filter: \" + filter.operation());\n                }\n            })\n            .orElse(s -> true);\n\n        //noinspection unchecked\n        return ArrayListTotal.of(\n            pageable,\n            decodedSecrets.keySet().stream().filter(queryPredicate).map(s -> (META) s).toList()\n        );\n    }\n\n    public Map<String, Set<String>> inheritedSecrets(String tenantId, String namespace) throws IOException {\n        return Map.of(namespace, decodedSecrets.keySet());\n    }\n\n    public Map<String, Set<String>> ownAndInheritedSecrets(String tenantId, String namespace) throws IOException {\n        return inheritedSecrets(tenantId, namespace);\n    }","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/kestra-io/kestra/blob/823fada9274c4f9c251ea0a516460a4f7d958032/core/src/main/java/io/kestra/core/secret/SecretService.java#L61-L97","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Use only `EQUALS` or `NOT_EQUALS` as the operation for a `QUERY` field filter when listing secrets.","If you need prefix or suffix matching, filter the returned list client-side.","If this is an SDK bug, report it — the API contract for secret listing should match."],"exampleFix":"// before\nfilters.add(QueryFilter.of(QueryFilter.Field.QUERY, QueryFilter.Op.STARTS_WITH, \"AWS\"));\n// after\nfilters.add(QueryFilter.of(QueryFilter.Field.QUERY, QueryFilter.Op.EQUALS, \"AWS\"));","handlingStrategy":"validation","validationCode":"// Validate the query filter operation before calling list()\nimport io.kestra.core.models.QueryFilter;\n\npublic static void validateSecretQueryFilters(List<QueryFilter> filters) {\n    for (QueryFilter f : filters) {\n        if (QueryFilter.Field.QUERY.equals(f.field())) {\n            if (f.operation() != QueryFilter.Op.EQUALS && f.operation() != QueryFilter.Op.NOT_EQUALS) {\n                throw new IllegalArgumentException(\n                    \"QUERY filter only supports EQUALS and NOT_EQUALS, got: \" + f.operation());\n            }\n        }\n    }\n}","typeGuard":"import { QueryFilter } from './types';\n\nfunction isSupportedSecretQueryOp(op: QueryFilter.Op): boolean {\n    return op === 'EQUALS' || op === 'NOT_EQUALS';\n}","tryCatchPattern":"try {\n    secretService.list(pageable, tenantId, filters);\n} catch (IllegalArgumentException e) {\n    if (e.getMessage().contains(\"Unsupported operation for QUERY filter\")) {\n        // strip unsupported filters and retry with EQUALS\n        filters = filters.stream()\n            .filter(f -> !QueryFilter.Field.QUERY.equals(f.field())\n                || f.operation() == QueryFilter.Op.EQUALS\n                || f.operation() == QueryFilter.Op.NOT_EQUALS)\n            .toList();\n        secretService.list(pageable, tenantId, filters);\n    } else throw e;\n}","preventionTips":["Only use EQUALS and NOT_EQUALS for QUERY filters when listing secrets.","Build a reusable filter-validation utility for all API calls.","Document the supported operations per filter field in the API contract."],"tags":["secret","query-filter","api-error","validation"],"backgroundTag":null,"analyzedSha":"823fada9274c4f9c251ea0a516460a4f7d958032","analyzedAt":"2026-08-14T06:15:17.947Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}