{"record":{"id":"585a3e54571ba164","repo":"spring-projects/spring-ai","slug":"unsupported-expression-type-s-only-not-is-suppor","errorCode":null,"errorMessage":"Unsupported expression type %s. Only NOT is supported here","messagePattern":"Unsupported expression type (.+?)\\. Only NOT is supported here","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverter.java","lineNumber":73,"sourceCode":"\t\t\tcase EQ -> \" = \";\n\t\t\tcase NE -> \" <> \";\n\t\t\tcase LT -> \" < \";\n\t\t\tcase LTE -> \" <= \";\n\t\t\tcase GT -> \" > \";\n\t\t\tcase GTE -> \" >= \";\n\t\t\tcase IN -> \" IN \";\n\t\t\tcase NOT, NIN -> \" NOT \";\n\t\t\t// you never know what the future might bring\n\t\t\tdefault -> throw new RuntimeException(\"Not supported expression type: \" + exp.type());\n\t\t};\n\t}\n\n\t@Override\n\tprotected void doNot(Expression expression, StringBuilder context) {\n\t\tFilter.ExpressionType expressionType = expression.type();\n\t\t// should not happen, but better safe than sorry\n\t\tif (expressionType != Filter.ExpressionType.NOT) {\n\t\t\tthrow new RuntimeException(\n\t\t\t\t\t\"Unsupported expression type %s. Only NOT is supported here\".formatted(expressionType));\n\t\t}\n\n\t\t// explicitly prefix the embedded expression with NOT\n\t\tcontext.append(\"NOT \").append(this.convertOperand(expression.left()));\n\t}\n\n\t@Override\n\tprotected void doKey(Key key, StringBuilder context) {\n\t\tString sanitized = SchemaNames.sanitize(\"metadata.\" + key.key(), true)\n\t\t\t.orElseThrow(() -> new IllegalArgumentException(\n\t\t\t\t\t\"Invalid or empty metadata key cannot be used in a Neo4j filter expression: '%s'\"\n\t\t\t\t\t\t.formatted(key.key())));\n\t\tcontext.append(\"node.\").append(sanitized);\n\t}\n\n\t@Override\n\tprotected void doStartGroup(Group group, StringBuilder context) {","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/vector-stores/spring-ai-neo4j-store/src/main/java/org/springframework/ai/vectorstore/neo4j/filter/Neo4jVectorFilterExpressionConverter.java#L55-L91","documentation":"Neo4jVectorFilterExpressionConverter.doNot is only designed to render Filter.Expression nodes of type NOT; any other type passed here indicates a malformed expression tree and triggers this RuntimeException. It is an internal invariant check ('should not happen, but better safe than sorry') inside the filter-to-Cypher conversion.","triggerScenarios":"A Filter.Expression with type != NOT is dispatched to doNot during conversion — only possible with a hand-constructed or corrupted Filter.Expression tree, or a custom FilterExpressionConverter misuse.","commonSituations":"Manually building Filter.Expression objects with wrong type/left operands; custom subclasses of FilterExpressionTextParser calling doExpression on invalid nodes; bugs after refactoring filter ASTs.","solutions":["Build expressions via the Filter builder API (Filter.eq(), Filter.not(), etc.) instead of raw constructors so the tree stays consistent.","Verify the expression passed to the parser has type NOT before expecting doNot handling.","Check for custom code that rewires Filter.Expression nodes and fix the type/operand assignment."],"exampleFix":"// before\nnew Filter.Expression(Filter.ExpressionType.AND, left, right) mistakenly routed to doNot\n// after\nnew Filter.Expression(Filter.ExpressionType.NOT, inner, null); // only NOT reaches doNot","handlingStrategy":"validation","validationCode":"static void assertNotTyped(Filter.Expression exp) {\n    if (exp.type() != Filter.ExpressionType.NOT) {\n        throw new IllegalArgumentException(\"doNot requires NOT expression\");\n    }\n}","typeGuard":"boolean isNotExpression(Filter.Expression exp) {\n    return exp != null && exp.type() == Filter.ExpressionType.NOT;\n}","tryCatchPattern":"try {\n    vectorStore.similaritySearch(request);\n} catch (RuntimeException e) {\n    if (e.getMessage().contains(\"Only NOT is supported here\")) {\n        throw new IllegalArgumentException(\"Malformed filter expression tree\", e);\n    }\n}","preventionTips":["Construct filters with the Filter builder API to keep the AST consistent","Never hand-build Filter.Expression nodes with mismatched type/left operands","Unit-test custom filter trees before deploying"],"tags":["neo4j","filter-expression","invariant"],"backgroundTag":"internal-invariant-violation","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}