Repository navigation
Conversation
exists() decided an FT.INFO error meant "missing index" by matching the message against a list of known phrasings. An unlisted phrasing made it raise instead of returning False, and because create() checks exists() first, the index was then never created (redis#773). redis-py raises a bare ResponseError for an ordinary error reply and a more specific exception for the replies that mean the check itself failed (NoPermissionError, ReadOnlyError, ClusterDownError, ...), while connection failures are not ResponseError at all. _is_missing_index_error now checks for exactly a bare ResponseError, so no list of phrasings is needed and a future rewording cannot break exists(). A misread can only produce False, never True, so create() never drops an index because of it; on a server without Redis Search, exists() returns False and create() raises the server's "unknown command" error.
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #773.
Motivation
exists()decides that anFT.INFOerror means "missing index" by matching the message against_MISSING_INDEX_ERROR_FRAGMENTS. A wording that isn't in the list makesexists()raise instead of returningFalse, and becausecreate()callsexists()first, the index is never created. The list already has to track three Redis Search wordings across versions, so each future rewording would breakexists()again until someone adds a line. This removes the list, as suggested in the issue thread.Changes
_is_missing_index_errorclassifies by exception type: it returnsTrueonly when the underlying redis-py exception is exactlyResponseError. redis-py raises a more specific type for the replies that mean the check itself failed (NoPermissionError,ReadOnlyError,ClusterDownError,AuthenticationError,BusyLoadingError, and so on), and connection failures aren'tResponseErrorat all, so those still raise._MISSING_INDEX_ERROR_FRAGMENTSis gone.SearchIndex.exists,AsyncSearchIndex.existsand the MCP server's schema loader all go through the one function, so none of them change.docs/api/exceptions.rstdescribes the type-based check instead of message matching.tests/unit/test_index_exists.py: the permission case now raisesNoPermissionError, which is what redis-py raises for a realNOPERMreply;ReadOnlyErrorandClusterDownErrorjoin the must-raise cases; and a wording no Redis version sends joins the must-return-Falsecases.Release Notes
SearchIndex.exists()andAsyncSearchIndex.exists()no longer depend on the wording of Redis Search's missing-index error, socreate()keeps working if that wording changes again. On a server without Redis Search,exists()now returnsFalseinstead of raising;create()still raises the server'sunknown commanderror.Reviewer Brief
Start here:
redisvl/exceptions.py,_is_missing_index_error. It is now one type check, and every missing-index decision in the library goes through it.Where it could fail
redisvl/exceptions.py:107: any plain error reply toFT.INFOnow reads as "missing". A misread can only giveFalse, socreate(overwrite=True)never drops on it;create()then fails onFT.CREATEwith the server's real error. Check that noFT.INFOreply for an existing index comes back as a bareResponseError.redisvl/mcp/server.py:1001: on a server without Redis Search, the MCP server now reports "Configured Redis index ... does not exist" rather than theunknown commanderror.Not verified: cluster mode (the cluster path re-raises the node's exception unchanged in redis-py, but no cluster was run); Redis 8.8's
SEARCH_INDEX_NOT_FOUNDreply live (covered by the unit test only).Beyond CI: live on Redis 8.2.10 with redis-py 8.1.0: missing index →
False, aftercreate()→True, an ACL user with-@search→ raisesNoPermissionError, async missing index →False; on a server without Redis Search (Valkey 8),exists()→Falseandcreate()raisesunknown command 'FT.CREATE'. Without the change, the two new must-return-Falseunit tests fail. redis-py 6.3.0 (the floor) and 8.1.0 mapNOPERM,READONLY,CLUSTERDOWN,TRYAGAIN,MASTERDOWN,NOAUTHandLOADINGto specific exception types.Reviewed
87501fe· Claude Code (claude-opus-5-5) · lenses: correctness, tests, docsNote
Medium Risk
Any future bare
ResponseErrorfromFT.INFOon an existing index would be misclassified as missing (safe forcreate()but could affect overwrite paths); behavior on servers without Search and MCP messaging also shifts slightly.Overview
SearchIndex.exists()/AsyncSearchIndex.exists()no longer treat a missing index by matching Redis Search error strings._is_missing_index_errornow returns true only when the chained redis-py cause is exactly a plainResponseError, so new Redis wordings (issue #773) still yieldFalseinstead of breakingcreate().Permission, cluster, and connection failures stay distinct: redis-py’s typed errors (
NoPermissionError,ReadOnlyError,ClusterDownError, etc.) and non-ResponseErrorfailures are still re-raised, not misreported as absent. Servers without Redis Search (unknown command) also returnFalsefromexists();create()still surfaces the realFT.CREATEerror.Docs in
docs/api/exceptions.rstdescribe the type-based rule; unit tests add unseen missing-index wording, typed non-missing cases, andNoPermissionErrorfor the “index named like an error phrase” scenario.Reviewed by Cursor Bugbot for commit 87501fe. Bugbot is set up for automated code reviews on this repo. Configure here.