{"record":{"id":"c447be93490422c2","repo":"spring-projects/spring-ai","slug":"vectortoolindex-does-not-support-categoryfilter","errorCode":null,"errorMessage":"VectorToolIndex does not support categoryFilter — '${categoryFilter}' will be ignored and results will not be narrowed by category.","messagePattern":"VectorToolIndex does not support categoryFilter — '(.+?)' will be ignored and results will not be narrowed by category\\.","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"spring-ai-tool-search-tool/src/main/java/org/springframework/ai/tool/toolsearch/index/vectorstore/VectorToolIndex.java","lineNumber":135,"sourceCode":"\t\t\tdocuments.add(new Document(id, ref.summary(), Map.of(METADATA_SESSION_ID, sessionId, METADATA_ID, id,\n\t\t\t\t\tMETADATA_TOOL_NAME, ref.toolName(), METADATA_TOOL_DESCRIPTION, ref.summary())));\n\t\t}\n\n\t\tthis.vectorStore.add(documents);\n\t\tthis.sessionToolIds.compute(sessionId, (k, existing) -> {\n\t\t\tList<String> list = existing != null ? new ArrayList<>(existing) : new ArrayList<>(ids.size());\n\t\t\tlist.addAll(ids);\n\t\t\treturn list;\n\t\t});\n\t\tif (logger.isInfoEnabled()) {\n\t\t\tlogger.info(\"Indexed \" + documents.size() + \" tools for sessionId=\" + sessionId);\n\t\t}\n\t}\n\n\t@Override\n\tpublic ToolSearchResponse search(ToolSearchRequest toolSearchRequest) {\n\t\tif (toolSearchRequest.categoryFilter() != null && logger.isWarnEnabled()) {\n\t\t\tlogger.warn(\"VectorToolIndex does not support categoryFilter — '\" + toolSearchRequest.categoryFilter()\n\t\t\t\t\t+ \"' will be ignored and results will not be narrowed by category.\");\n\t\t}\n\t\tint maxResults = toolSearchRequest.maxResults() != null ? toolSearchRequest.maxResults() : DEFAULT_MAX_RESULTS;\n\n\t\tList<Document> docs = this.doSearch(toolSearchRequest.query(), toolSearchRequest.sessionId(), maxResults,\n\t\t\t\tDEFAULT_SIMILARITY_THRESHOLD);\n\n\t\tList<ToolReference> toolReferences = docs.stream()\n\t\t\t.map(doc -> ToolReference.builder()\n\t\t\t\t.toolName((String) Objects.requireNonNull(doc.getMetadata().get(METADATA_TOOL_NAME)))\n\t\t\t\t.relevanceScore(Objects.requireNonNullElse(doc.getScore(), 0.0))\n\t\t\t\t.summary((String) Objects.requireNonNull(doc.getMetadata().get(METADATA_TOOL_DESCRIPTION)))\n\t\t\t\t.build())\n\t\t\t.toList();\n\n\t\treturn ToolSearchResponse.builder()\n\t\t\t.toolReferences(toolReferences)\n\t\t\t.totalMatches(toolReferences.size())","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-tool-search-tool/src/main/java/org/springframework/ai/tool/toolsearch/index/vectorstore/VectorToolIndex.java#L117-L153","documentation":"VectorToolIndex performs semantic similarity search over a vector store and does not implement category-based filtering. If a ToolSearchRequest carries a categoryFilter, it logs this warning and ignores the filter, returning results narrowed only by similarity — never by category.","triggerScenarios":"Calling search on a VectorToolIndex with ToolSearchRequest.builder().categoryFilter(\"...\").build(); the filter value is logged and discarded.","commonSituations":"Reusing request-building code shared with Lucene/Regex indexes that support categoryFilter; switching the index backend from LuceneToolIndex to VectorToolIndex assuming feature parity; users adding categories to metadata expecting filtering.","solutions":["Remove categoryFilter from requests sent to VectorToolIndex or accept unfiltered semantic results.","Switch to LuceneToolIndex or RegexToolIndex if category filtering is required.","Embed category information into tool descriptions and/or include category terms in the query so semantic search effectively narrows results.","Post-filter the returned ToolSearchResponse results by category in application code."],"exampleFix":"// before: filter silently ignored\nvectorToolIndex.search(ToolSearchRequest.builder().query(\"weather\").categoryFilter(\"travel\").build());\n// after: include category signal in query or post-filter\nvar resp = vectorToolIndex.search(ToolSearchRequest.builder().query(\"weather\").build());\nvar filtered = resp.tools().stream().filter(t -> \"travel\".equals(t.category())).toList();","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":["Don't set categoryFilter when using VectorToolIndex.","Choose LuceneToolIndex/RegexToolIndex when category filtering matters.","Bake category keywords into tool descriptions for semantic narrowing.","Post-filter results in application code as a fallback."],"tags":["tool-search","vector-store","unsupported-feature","filtering"],"backgroundTag":"unsupported-operation","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"}