{"record":{"id":"d673e21de754a55e","repo":"spring-projects/spring-ai","slug":"failed-to-delete-document-from-index","errorCode":null,"errorMessage":"Failed to delete document from index","messagePattern":"Failed to delete document from index","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"spring-ai-tool-search-tool/src/main/java/org/springframework/ai/tool/toolsearch/index/lucene/LuceneToolIndex.java","lineNumber":296,"sourceCode":"\t\t\t.toolReferences(foundToolReferences)\n\t\t\t.totalMatches(foundToolReferences.size())\n\t\t\t.searchMetadata(SearchMetadata.builder().searchType(this.getClass().getSimpleName()).query(query).build())\n\t\t\t.build();\n\t}\n\n\t/**\n\t * Deletes a tool from the index for the specified session by its ID.\n\t * @param sessionId the session ID\n\t * @param id the tool ID to delete\n\t */\n\tpublic void delete(String sessionId, String id) {\n\t\tSessionIndex sessionIndex = this.sessionIndexes.get(sessionId);\n\t\tif (sessionIndex != null) {\n\t\t\ttry {\n\t\t\t\tsessionIndex.writer.deleteDocuments(new Term(FIELD_ID, id));\n\t\t\t}\n\t\t\tcatch (IOException e) {\n\t\t\t\tthrow new RuntimeException(\"Failed to delete document from index\", e);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Returns the number of documents in the index for the specified session.\n\t * @param sessionId the session ID\n\t * @return document count, or 0 if session not found\n\t */\n\tpublic int size(String sessionId) {\n\t\tSessionIndex sessionIndex = this.sessionIndexes.get(sessionId);\n\t\tif (sessionIndex == null) {\n\t\t\treturn 0;\n\t\t}\n\t\ttry {\n\t\t\treturn sessionIndex.ensureAndGetReader().numDocs();\n\t\t}\n\t\tcatch (IOException e) {","sourceCodeStart":278,"sourceCodeEnd":314,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/spring-ai-tool-search-tool/src/main/java/org/springframework/ai/tool/toolsearch/index/lucene/LuceneToolIndex.java#L278-L314","documentation":"Thrown by LuceneToolIndex.delete(sessionId, id) when IndexWriter.deleteDocuments(new Term(FIELD_ID, id)) throws an IOException. The delete is buffered in the writer until commit, so this error means Lucene could not even enqueue the delete (typically an I/O problem with the index directory or a closed writer), not that the document was absent.","triggerScenarios":"Calling delete() for an existing session whose index directory is unwritable/full or whose files are corrupt; the session's writer was closed by a concurrent clearIndex() (write-after-close race); or the index directory was deleted externally before the delete operation.","commonSituations":"Disk-full while removing segments during merges; read-only filesystem after redeployment; concurrent clearIndex() closing the writer mid-delete; index directory on unstable network storage; a crashed process leaving a stale write lock so the reopened writer fails on I/O.","solutions":["Check the wrapped IOException cause to pinpoint the failing Lucene operation","Verify disk space and write permissions on the index directory, then retry delete()","If the writer was concurrently closed by clearIndex(), the documents are already gone — treat the operation as complete","If the index is corrupt, delete the session's index directory, re-add remaining tools, and commit","Serialize delete/clearIndex calls for the same session to avoid the documented race"],"exampleFix":"// before\nindex.delete(sessionId, toolId); // RuntimeException on IO failure\n// after\ntry {\n    index.delete(sessionId, toolId);\n}\ncatch (RuntimeException e) {\n    logger.error(\"failed to delete tool \" + toolId + \" from session \" + sessionId, e);\n    // check disk space; if session was concurrently cleared the tool is already removed\n}","handlingStrategy":"try-catch","validationCode":"if (!index.hasSession(sessionId)) {\n    return; // delete is a no-op: session index does not exist\n}\nFile idxDir = new File(indexPath);\nif (!Files.isWritable(idxDir.toPath())) {\n    throw new IllegalStateException(\"Index directory not writable: \" + indexPath);\n}","typeGuard":null,"tryCatchPattern":"try {\n    index.delete(sessionId, toolId);\n}\ncatch (RuntimeException e) {\n    if (e.getCause() instanceof java.io.IOException ioEx) {\n        logger.error(\"delete failed for tool \" + toolId, ioEx);\n        // if session was concurrently cleared the tool is already gone; otherwise check disk and retry\n    }\n}","preventionTips":["Skip the delete call when the session index does not exist (it is already a no-op)","Serialize delete/clearIndex calls per session to avoid the write-after-close race","Keep free disk space above a threshold; deletes can trigger segment merges","Commit after deletes if persistence before process exit matters"],"tags":["lucene","io","delete","index"],"backgroundTag":"file-write-failed","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}