@@ -107,6 +107,78 @@ async def test_init_search_index(search_repository, app_config):
107107 assert table_name == "search_index"
108108
109109
110+ @pytest .mark .asyncio
111+ async def test_init_search_index_degrades_when_extension_loading_unavailable (
112+ search_repository , monkeypatch
113+ ):
114+ """Regression for #711: when sqlite-vec cannot be loaded (e.g. python.org Python
115+ 3.12 ships sqlite3 without enable_load_extension), init must NOT crash. It should
116+ log a warning, mark the repository as semantic-disabled, and let the rest of the
117+ process come up so Claude Desktop's MCP handshake completes."""
118+ if is_postgres_backend (search_repository ):
119+ pytest .skip ("python.org enable_load_extension issue is SQLite-specific" )
120+
121+ from basic_memory .repository .semantic_errors import SemanticDependenciesMissingError
122+
123+ # Force the codepath even if semantic_search wasn't enabled by default.
124+ search_repository ._semantic_enabled = True
125+
126+ async def _raise_missing ():
127+ raise SemanticDependenciesMissingError ("simulated: enable_load_extension missing" )
128+
129+ monkeypatch .setattr (search_repository , "_ensure_vector_tables" , _raise_missing )
130+
131+ # Must not raise — startup needs to complete even when the semantic stack is dead.
132+ await search_repository .init_search_index ()
133+
134+ assert search_repository ._semantic_enabled is False , (
135+ "Repository should mark itself semantic-disabled after a missing-deps error "
136+ "so downstream calls short-circuit cleanly instead of re-attempting load."
137+ )
138+
139+
140+ @pytest .mark .asyncio
141+ async def test_ensure_sqlite_vec_loaded_raises_typed_error_without_extension_support (
142+ search_repository , monkeypatch
143+ ):
144+ """Regression for #711: AttributeError from a sqlite3.Connection that lacks
145+ enable_load_extension must surface as SemanticDependenciesMissingError so the
146+ init-time handler can degrade. Otherwise the AttributeError bubbles through and
147+ crashes startup before Claude Desktop completes its handshake."""
148+ if is_postgres_backend (search_repository ):
149+ pytest .skip ("enable_load_extension is SQLite-specific" )
150+
151+ from basic_memory .repository .semantic_errors import SemanticDependenciesMissingError
152+ from sqlalchemy .exc import OperationalError as SAOperationalError
153+
154+ # Stub session that always reports vec missing on probe, then yields a connection
155+ # whose driver_connection has no enable_load_extension attribute (mirroring the
156+ # python.org sqlite3 build).
157+ class _StubDriverConnection :
158+ # Deliberately omit enable_load_extension to mimic the python.org build.
159+ pass
160+
161+ class _StubRawConnection :
162+ driver_connection = _StubDriverConnection ()
163+
164+ class _StubAsyncConnection :
165+ async def get_raw_connection (self ):
166+ return _StubRawConnection ()
167+
168+ class _StubSession :
169+ async def execute (self , _stmt ):
170+ # First (and any) probe call reports vec missing.
171+ raise SAOperationalError ("SELECT vec_version()" , {}, Exception ("no vec" ))
172+
173+ async def connection (self ):
174+ return _StubAsyncConnection ()
175+
176+ with pytest .raises (SemanticDependenciesMissingError ) as exc_info :
177+ await search_repository ._ensure_sqlite_vec_loaded (_StubSession ())
178+
179+ assert "enable_load_extension" in str (exc_info .value )
180+
181+
110182@pytest .mark .asyncio
111183async def test_init_search_index_preserves_data (search_repository , search_entity ):
112184 """Regression test: calling init_search_index() twice should preserve indexed data.
0 commit comments