MemPalace/mempalace · error · BackendClosedError
QdrantBackend has been closed
Error message
QdrantBackend has been closed
What it means
Raised by QdrantBackend._client() when the backend instance has been closed (close() called) and any operation needs an HTTP client. All collections share the backend's client cache, so after close() every subsequent call through this backend fails fast rather than attempting HTTP on a torn-down object.
Source
Thrown at mempalace/backends/qdrant.py:1303
# presence signals "palace initialized" (reads raise CollectionNotInitialized
# when the marker exists but the remote collection doesn't), so recording
# identity at first empty open must not create it. The sidecar is unguarded,
# so a brand-new palace can record identity immediately.
@staticmethod
def _embedder_sidecar_path(palace: PalaceRef) -> Optional[str]:
if not palace.local_path:
return None
return os.path.join(palace.local_path, EMBEDDER_SIDECAR_FILENAME)
def _get_embedder_identity(self, palace: PalaceRef, collection_name: str):
return read_embedder_sidecar(self._embedder_sidecar_path(palace), collection_name)
def _set_embedder_identity(self, palace: PalaceRef, collection_name: str, identity) -> None:
write_embedder_sidecar(self._embedder_sidecar_path(palace), collection_name, identity)
def _client(self, config: _QdrantConfig) -> _QdrantRESTClient:
if self._closed:
raise BackendClosedError("QdrantBackend has been closed")
with self._lock:
client = self._clients.get(config)
if client is None:
client = _QdrantRESTClient(config)
self._clients[config] = client
return client
def _remote_collection_name(
self,
*,
palace: PalaceRef,
collection_name: str,
config: _QdrantConfig,
) -> str:
config = _QdrantConfig(
url=config.url,
api_key=config.api_key,
timeout=config.timeout,View on GitHub (pinned to 06cb6987f0)
Solutions
- Re-open the backend (get_backend('qdrant') returns a fresh instance after reset/close) and re-fetch collections before using them
- Fix lifecycle ordering so no threads/callbacks use the backend after close()
- In tests, obtain collection handles inside each test after backend setup rather than caching across reset_backends()
Example fix
// before
backend.close()
col.count() # BackendClosedError via _client()
// after
backend.close()
backend = get_backend("qdrant") # fresh instance
col = backend.get_collection(palace, "drawers")
col.count() Defensive patterns
Strategy: try-catch
Try / catch
from mempalace.backends.base import BackendClosedError
try:
col.count()
except BackendClosedError:
backend = get_backend("qdrant")
col = backend.get_collection(palace, name)
col.count() Prevention
- Close backends exactly once at process exit, after all workers stop
- Don't cache collection handles across backend resets in tests
- Add a is-closed guard in background tasks before touching the backend
When it happens
Trigger: Calling any collection operation after backend.close() (or reset_backends() in tests) — including via a collection handle obtained before closing, since collections hold the backend reference. Also hit when a test fixture closes the backend in teardown but a lingering handle is used afterwards.
Common situations: App shutdown ordering: background thread/hook fires after the main path closed the backend; tests that call reset_backends() for isolation then reuse a stale collection object; keeping a module-level collection handle across a CLI re-invocation in the same process.
Related errors
- MilvusCollection has been closed
- QdrantCollection has been closed
- PgVectorBackend has been closed
- operator {key!r} not supported by qdrant
- operator {op!r} not supported by qdrant
AI-assisted analysis of MemPalace/mempalace@06cb6987f0 (2026-08-15).
Data as JSON: /api/errors/c6632fbb545f2c58.
Report an issue: GitHub.