diff --git a/semantic_router/index/base.py b/semantic_router/index/base.py
index 452a18c65d24eecd4a3d3f12eecd75b1fae1df43..933e22942d592cb8515869ddea9217b6089e17fc 100644
--- a/semantic_router/index/base.py
+++ b/semantic_router/index/base.py
@@ -160,6 +160,13 @@ class BaseIndex(BaseModel):
         """
         raise NotImplementedError("This method should be implemented by subclasses.")
 
+    def is_ready(self) -> bool:
+        """
+        Checks if the index is ready to be used.
+        This method should be implemented by subclasses.
+        """
+        raise NotImplementedError("This method should be implemented by subclasses.")
+
     def query(
         self,
         vector: np.ndarray,
diff --git a/semantic_router/index/local.py b/semantic_router/index/local.py
index 10b77bea8772c4b4716e1b46fb970cbdd5a49771..61b2c3b5ffd5110048de8c7b5f8edd747734e5c3 100644
--- a/semantic_router/index/local.py
+++ b/semantic_router/index/local.py
@@ -82,6 +82,12 @@ class LocalIndex(BaseIndex):
             vectors=self.index.shape[0] if self.index is not None else 0,
         )
 
+    def is_ready(self) -> bool:
+        """
+        Checks if the index is ready to be used.
+        """
+        return self.index is not None and self.routes is not None
+
     def query(
         self,
         vector: np.ndarray,
diff --git a/semantic_router/index/pinecone.py b/semantic_router/index/pinecone.py
index b07063186198ff7bf24d1418f8327a6f295fe58e..f885fbf757b8223d1a12b2cf68d89a55bbb74937 100644
--- a/semantic_router/index/pinecone.py
+++ b/semantic_router/index/pinecone.py
@@ -464,6 +464,12 @@ class PineconeIndex(BaseIndex):
                 vectors=0,
             )
 
+    def is_ready(self) -> bool:
+        """
+        Checks if the index is ready to be used.
+        """
+        return self.index is not None
+
     def query(
         self,
         vector: np.ndarray,
diff --git a/semantic_router/index/postgres.py b/semantic_router/index/postgres.py
index 54054c848459a6b01488c7fee2b8a5f0a2ed8042..6f4a9f2a1e56eaaeb890e5850c79acb0d61039ac 100644
--- a/semantic_router/index/postgres.py
+++ b/semantic_router/index/postgres.py
@@ -352,6 +352,12 @@ class PostgresIndex(BaseIndex):
                 vectors=count,
             )
 
+    def is_ready(self) -> bool:
+        """
+        Checks if the index is ready to be used.
+        """
+        return isinstance(self.conn, psycopg2.extensions.connection)
+
     def query(
         self,
         vector: np.ndarray,
diff --git a/semantic_router/index/qdrant.py b/semantic_router/index/qdrant.py
index 5986f2c0c71d8ee9e26e6d21b93c48da26d5f8da..5b2eac80d2914163cd7b9247ebd55bbdb5c74266 100644
--- a/semantic_router/index/qdrant.py
+++ b/semantic_router/index/qdrant.py
@@ -196,6 +196,10 @@ class QdrantIndex(BaseIndex):
             List[Tuple]: A list of (route_name, utterance, function_schema, metadata) objects.
         """
 
+        # Check if collection exists first
+        if not self.client.collection_exists(self.index_name):
+            return []
+
         from qdrant_client import grpc
 
         results = []
@@ -255,6 +259,12 @@ class QdrantIndex(BaseIndex):
             vectors=collection_info.points_count,
         )
 
+    def is_ready(self) -> bool:
+        """
+        Checks if the index is ready to be used.
+        """
+        return self.client.collection_exists(self.index_name)
+
     def query(
         self,
         vector: np.ndarray,
diff --git a/semantic_router/routers/base.py b/semantic_router/routers/base.py
index c551b124fa5aee12a1f3c544dc9fbcec755180cd..e42f16305781583163001d276cfc908f74b5cedd 100644
--- a/semantic_router/routers/base.py
+++ b/semantic_router/routers/base.py
@@ -422,9 +422,8 @@ class BaseRouter(BaseModel):
         simulate_static: bool = False,
         route_filter: Optional[List[str]] = None,
     ) -> RouteChoice:
-        ready = self._index_ready()
-        if not ready:
-            raise ValueError("Index or routes are not populated.")
+        if not self.index or not self.index.is_ready():
+            raise ValueError("Index is not ready.")
         # if no vector provided, encode text to get vector
         if vector is None:
             if text is None:
@@ -481,9 +480,9 @@ class BaseRouter(BaseModel):
         simulate_static: bool = False,
         route_filter: Optional[List[str]] = None,
     ) -> RouteChoice:
-        ready = self._index_ready()  # TODO: need async version for qdrant
-        if not ready:
-            raise ValueError("Index or routes are not populated.")
+        if not self.index or not self.index.is_ready():
+            # TODO: need async version for qdrant
+            raise ValueError("Index is not ready.")
         # if no vector provided, encode text to get vector
         if vector is None:
             if text is None:
diff --git a/semantic_router/routers/hybrid.py b/semantic_router/routers/hybrid.py
index 3d810576855ffa791e8dcf1056053aebc489c7ed..cb8b5f51b440e429b12983a20399ecbfb778d312 100644
--- a/semantic_router/routers/hybrid.py
+++ b/semantic_router/routers/hybrid.py
@@ -218,8 +218,8 @@ class HybridRouter(BaseRouter):
         route_filter: Optional[List[str]] = None,
         sparse_vector: dict[int, float] | SparseEmbedding | None = None,
     ) -> RouteChoice:
-        if self.index.index is None or self.routes is None:
-            raise ValueError("Index or routes are not populated.")
+        if not self.index or not self.index.is_ready():
+            raise ValueError("Index is not ready.")
         potential_sparse_vector: List[SparseEmbedding] | None = None
         # if no vector provided, encode text to get vector
         if vector is None:
diff --git a/tests/unit/test_router.py b/tests/unit/test_router.py
index 799a978d296c77dd486d572d8b99f5323d115fb0..985898473397722cbc16845840a7a92641df56ac 100644
--- a/tests/unit/test_router.py
+++ b/tests/unit/test_router.py
@@ -282,7 +282,7 @@ class TestIndexEncoders:
             try:
                 assert len(route_layer.index) == 5
                 break
-            except AssertionError:
+            except Exception:
                 logger.warning(f"Index not populated, waiting for retry (try {count})")
                 time.sleep(PINECONE_SLEEP)
                 count += 1
@@ -733,7 +733,7 @@ class TestSemanticRouter:
             try:
                 assert query_result in ["Route 1", "Route 2"]
                 break
-            except AssertionError:
+            except Exception:
                 logger.warning(
                     f"Query result not in expected routes, waiting for retry (try {count})"
                 )
@@ -770,7 +770,7 @@ class TestSemanticRouter:
             try:
                 assert query_result in ["Route 1"]
                 break
-            except AssertionError:
+            except Exception:
                 logger.warning(
                     f"Query result not in expected routes, waiting for retry (try {count})"
                 )
@@ -800,7 +800,7 @@ class TestSemanticRouter:
                     ).name
                     assert query_result in ["Route 1"]
                     break
-                except AssertionError:
+                except Exception:
                     logger.warning(
                         f"Query result not in expected routes, waiting for retry (try {count})"
                     )
@@ -830,7 +830,11 @@ class TestSemanticRouter:
         if index_cls is PineconeIndex:
             time.sleep(PINECONE_SLEEP)  # allow for index to be populated
         vector = encoder(["hello"])
-        query_result = route_layer(vector=vector).name
+        if router_cls is HybridRouter:
+            sparse_vector = route_layer.sparse_encoder(["hello"])[0]
+            query_result = route_layer(vector=vector, sparse_vector=sparse_vector).name
+        else:
+            query_result = route_layer(vector=vector).name
         assert query_result in ["Route 1", "Route 2"]
 
     def test_query_with_no_text_or_vector(